前言❤️❤️
hello hello💕,这里是洋不写bug~😄,欢迎大家点赞👍👍,关注😍😍,收藏🌹🌹
日常使用数据库时,很容易不小心存入错误数据(比如给 "年龄" 列存负数、给 "手机号" 列存非数字)
数据库约束就是在创建表时,为列(甚至整张表)设置的规则限制,它能从源头拦截不符合规则的数据写入 ,一旦尝试存错误数据,数据库就会直接报错并拒绝操作,这部分知识是数据库的一个重点,铁汁们在学习时要多练习🐵
🎇个人主页:洋不写bug的博客
🎇所属专栏:数据库
🎇mysql8.0和navicate的安装:mysql安装教程
🎇铁汁们对于MySQL数据库的各种常用核心语法,都可以在上面的数据库专栏学习,专栏正在持续更新中🐵🐵,有问题可以写在评论区或者私信我哦~
1,约束简介
有一个学生信息的数据库,这个数据库里面有很多表,比如成绩表,发奖学金的表,还有体测表等等,那这些表之间就是通过学号关联在一起的(因为名字可能会有重复,而学号是不会的)
那这时候如果在成绩表中存学号时,手抖打错了,那就可能出现两个人学号一样的情况,那这时候不仅成绩表中一个人的数据本身是错误的,而且表之间的关联关系也是错误的(根据学号查两个人的体测成绩,那么一个人的就是错的)
为了避免这个问题,就需要使用数据库约束,例如在定义表的时候就约束学号不能两个人的相同,让数据库自动帮我们去检查还可以对表中的数据进行很多约束,不只是唯一性,还可以约束非空,限制值等等,如下图:

2,NOT NULL 非空约束
也就是约束这列的值不能为空,我们如果能预测某个列的值是不可能为空的,比如学号,年龄,那非常建议定义表时对这列加上非空约束,这样能提高查询效率。
例如创建一个学生表,定义名字不为null,尝试插入null,就会报错,提示name不能为null
sql
CREATE table student(
id BIGINT,
`name` VARCHAR(32) not null
)

3,UNIQUE 唯一约束
约束一列中不能出现两个重复的数据,例如身份证号,学号之类的
跟NOT NULL用法类似,这里再创建一个叫student1的表,给id列加上UNIQUE约束,在表中加一个id为1的学生
sql
CREATE table student1(
id BIGINT UNIQUE,
`name` VARCHAR(32) not null
)
INSERT INTO student1(id,`name`) VALUES(1,"mark");
当我们尝试插入一个学号相同的学生时,就会报错,原因是键'student1.id' 存在重复条目 '1'(这里的key就是键的意思)

但是如果这样写,明明id重复了,都是null,却不会报错:

查询表,就会发现这些数据确实是插入了进去,这是因为null不在unique的判断范围内

4,PRIMARY KEY 主键约束
主键的4大特征:
- 主键约束唯一标识数据库表中的每条记录。(也就是唯一标识数据行的身份信息)
- 主键必须包含唯一的值,且不能包含 NULL 值。
- 每个表只能有一个主键,可以由单个列或多个列组成。
- 通常为每张表都指定一个主键,主键列建议使用 BIGINT 类型(因为这样数据表就算有很多的行,也能进行区分)
sql
CREATE TABLE student(
id BIGINT primary key,
name varchar(20) not null
)
创建一个学生表,把id作为这个表的主键,查看下表的结构,就会发现这里以及标注id是主键

按照我们前面说的主键的特性,这个id就不能是空,也不能重复,先插入一行正常数据,再进行测试。再插入重复数据,就会报错
sql
INSERT INTO student(id,`name`) VALUES(1,"mark");

插入空值,也会报错
sql
INSERT INTO student(id,`name`) VALUES(NULL,"carrier");

那铁汁们想一下,如果这样写,跟写成主键,是一样的吗???
sql
CREATE TABLE student(
id BIGINT NOT NULL UNIQUE,
name varchar(20) not null
)
其实是不一样的,我们看前面对定义为主键的表的结构的打印,主键是有特殊的标记的,而不只是非空且唯一,前面说了4条主键的特性,非空且唯一只是其中的一个(如下图)

写非空且唯一仅仅是对这列数据的需求是这样
主键是数据表的一个特性,数据如何组织就是依赖主键的,这个了解一下即可

在表中既然插入主键重复值就会报错,那要是有个学生表,以id作为主键,我们后面可能会更新一个学生的数据,但是不确定这个学生原来在表里面是否存在,就可以提前进行预测,如果主键不冲突就正常插入,主键冲突的话就更新数据: (这个也不是很常用,大家了解下即可)
sql
CREATE TABLE student(
id BIGINT primary key,
`name` varchar(20) not null,
num BIGINT
)
INSERT INTO student(id,`name`,num) VALUES(1,"marrie",10001);
sql
INSERT INTO student(id,`name`,num) VALUES(1,"marrie",10100)
on DUPLICATE key UPDATE name = "marrie",num = 10100;
查询看下,这行数据已经被成功更新了

5,主键自增
在定义主键时往往把主键列设置为自动增长,让数据库去维护主键列 ,这样就比较方便
把前面的student表删一下,再重新写个表,在主键后面加个auto_increment(auto是自动的意思,increment是增加的意思,合起来就是自增)
插入三条数据,只插入name,不管id
sql
drop table student;
sql
CREATE TABLE student(
id BIGINT primary key AUTO_INCREMENT,
name varchar(20) not null
)
sql
INSERT INTO student(`name`) VALUES("carrie");
INSERT INTO student(`name`) VALUES("helena");
INSERT INTO student(`name`) VALUES("helly");
看下表结构,就会发现数据库会帮我们去维护这个主键列

有个特殊情况,就是当我们定义主键列自增后,再给主键列插入null,并不会报错,数据库会将 NULL 解读为「请求自动生成值」,这样既避免了手动指定值的麻烦,又保证了主键的非空性和唯一性
sql
INSERT INTO student(id,`name`) VALUES(NULL,"mark");

主键是这样的:不指定id时,数据库会帮我们自动管理,指定了就按指定的
那如果手动插入了一个id为100的数据,那下次插入数据再让数据库自动管理,那插入的是101,还是5呢?
sql
INSERT INTO student(id,`name`) VALUES(100,"oven");

这里插入一个数据看一下,就会发现是从101开始了 :

为了搞清楚这个问题,再指定插入一个id为5的这样一行数据,如果接下来不指定id插入数据,数据库自动管理,那插入的id是6,还是102呢?

再插条数据试下,会发现id其实是从102开始的

如果在mysql命令行工具里写上show create table student;(在NAVICAT中写结构显示很简单,基本上看不出来什么)
就会出现这个表的具体结构,如果我们使用主键自增的话,那这里就会显示下一次插入时的自增值

这个自增值只能增加,不能回退 ,因为回退后如果新插入数据的话,就有可能会把以前的数据给覆盖掉(也可以理解成自增值就是当前表中id最大值的下一位)
6,复合主键
前面提到,每个列只能有一个主键
如下,定义一个表时,同时两个列作为主键,就会报错:定义了重复的主键

那如果现在有个订货数据表,有订单编号,供应商编号,还有这批货的价格
不同供货商的订单编号存在相同,不能当作主键,价格由订单编号和供货商编号共同决定
这里就可以把这两列组合起来,共同成为一个主键,具体这样写:
sql
CREATE TABLE `order`(
order_number BIGINT,
vendor_id BIGINT,
price DOUBLE,
PRIMARY KEY(order_number,vendor_id)
)
插入一条数据
sql
INSERT INTO `order`(order_number,vendor_id,price) VALUES(22,3,999);
接着无论插入订单编号相同,供货商编号不同,还是插入供货商编号相同,订单编号不同的数据,都不会报错(如下图)
因为主键是两者组合在一起的,这样并没有重复。

但是插入一个订单编号和供货商编号都跟原来相同的数据,就报错了
仔细看下报错信息,就会发现:这里的主键是两列组合在一起的(22-3)

那复合主键是一个列为null就报错,还是两个列都为null才报错呢,再来测试一下:


结果是一个列为null就会报错,也就是复合主键下的两个列都不能为null
7,外键约束
外键约束用于定义主表和从表的关系
下面两个表,哪个是主表,哪个是从表呢?

这里博主有个比较好用的方法,可以通过看表的依赖关系来判断,也就是假设一个表没有了,另一个表中的记录是否还有效。
这里学生表没有了,班级表中记录的数据仍然都有效,但是如果班级表没了,那学生表中记录班级的数据就无效了,因为无法确定这些学生是哪个班级的。
因此,学生表是依赖班级表的,学生表就是从表,班级表就是主表
这里有的铁汁会有疑问,那直接在学生表直接写上每个学生的班级就可以了,为什么还要这么麻烦,搞两个表。假设学生表里有1000个学生,有400个报的是唱歌班,假设后面唱歌班的名字改为了合唱班,如果只用一个表的话,那就要改400个数据;而用两个表,就只需要改1个数据,在班级表里把唱歌班改为合唱班就可以了。
因此设计主从表在一些修改场景下修改会方便很多。
再把之前创建的student表删了,创建student表(从表)和class表(主表),实现主从表的关系
sql
CREATE table class(
id BIGINT PRIMARY KEY,
name VARCHAR(20) not NULL
)
CREATE table student(
id BIGINT PRIMARY KEY auto_increment,
name varchar(20) not NULL,
gender VARCHAR(5) not NULL,
class_id BIGINT,
foreign key (class_id) REFERENCES class(id)
)
这里要在从表的最后选择哪个列创建为外键,还要加上外键在主表中的引用

先把class(班级表)的数据写进去:
sql
INSERT INTO class(id,name) VALUES(1,"美容班"),(2,"厨师班"),
(3,"武术班"),(4,"唱歌班"),(5,"跳舞班");

这里尝试往student表中写入一个班级编号为6的学生数据,这个班级编号在主表里没有对应,那就会报错

仔细看下报错信息,这个报错就是外键约束冲突错误:

接下来往student表里插入一些数据
sql
INSERT INTO student(id,name,gender,class_id)
VALUES(1,"懒羊羊","男",1),(3,"沸羊羊","男",2),(4,"美羊羊","男",3);

这时候student表是依赖class表的,那这时候如果尝试删除class表中的数据,因为有外键的校验,也会报错:

当然,尝试删除整个class表也不行:

但是class表中id为5这一行的数据没有来自从表的引用,因此可以正常删除

删除主表时如果从表中有对主表记录的引用,则不允许删除主表中对应的内容。如果必须要删除主表,那就要先删除从表中的数据或外键关系。
这里先删除下从表,主表的数据就可以删除了
sql
drop table student;

不想删student表的话,也可以这样写,解除外键关系(这个外键名是自动生成的,在前面的报错信息那里就能看到),这个也是很少使用,大家了解下即可
sql
ALTER TABLE student drop FOREIGN key student_ibfk_1;
注:我们在工作中在数据库层面不创建外键,因为数据库的数据量太大了,而是在Java层面处理关联关系,保证数据正确,校验完了再入库。
8, DEFAULT默认值约束
这个其实特别简单,意思就是我们没有给某列设置值,就会填入默认值
这里创建一个student表,如果我们在插入数据时没有设置年龄值,那这里年龄就是null
sql
CREATE table student(
id bigint,
name varchar(20) not null,
age int
);
INSERT INTO student(id,name) VALUES(1,"刘备");

删除一下student表,创建时加上默认值约束(default后面加上数字18),这时候没有设置年龄值时年龄就会设为默认值,如下图:
sql
drop table student;
CREATE table student(
id bigint,
name varchar(20) not null,
age int DEFAULT 18
);
INSERT INTO student(id,name) VALUES(1,"刘备");

10,CHECK 约束
check约束可以应用于一个列或者多个列,用于限制列中的数据,从而保证数据的完整性和准确性
在mysql8.0.16版本后才开始支持check约束,之前的版本并不支持,但是写上也不会报错
重构一下学生表,加上check约束:
sql
CREATE table student(
id bigint PRIMARY KEY AUTO_INCREMENT,
name varchar(20) not null,
age int DEFAULT(18),
gender char(1),
check(age >= 16),
check(gender = '男' or gender = '女')
);
接着插入不满足约束的数据尝试下,就会报错(这里的student_chk_1就是自动生成的约束名)


还可以在定义列名时就进行限制,也可以限制列跟列之间的关系,示例如下(这里的<>代表不等于)
sql
create table t_check(
c1 int check(c1 <> 0),
c2 int check(c2 > 0),
c3 int,
CHECK(c3 >= c2)
);



11,总结
在工作中,使用频率最高的三个就是 NOT NULL非空约束、UNIQUE唯一约束、PRIMARY KEY主键约束,使用这三个约束不仅能保证数据的准确性和完整性,还可以有效的提升查询效率
CHECK约束一般就直接在Java程序(C++)程序中实现了,不写在sql语句中
FOREIGN KEY外键约束比较影响查询效率,在大型互联网项目中很少使用
