提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 约束
-
- 约束的作用
- 约束的类型
- 非空约束
- 唯一性约束
- 主键约束
- 默认值约束
- 检查约束
- 指定检查约束
- 外键约束
-
- 指定外键约束
- 查看外键约束
- 查看外键字段索引
- 删除外键约束
- 设置外键约束等级
- 总结
-
- **一、约束的作用**
- **二、约束的分类**
-
- [1. 按约束类型划分](#1. 按约束类型划分)
- [2. 按约束级别划分](#2. 按约束级别划分)
- **三、约束与索引的区别(易错点高频区)**
- **四、各类约束详细说明**
- [1. 非空约束(NOT NULL)](#1. 非空约束(NOT NULL))
- [2. 唯一性约束(UNIQUE)](#2. 唯一性约束(UNIQUE))
- [3. 主键约束(PRIMARY KEY)](#3. 主键约束(PRIMARY KEY))
- [4. 自增约束(AUTO_INCREMENT)](#4. 自增约束(AUTO_INCREMENT))
- [5. 默认值约束(DEFAULT)](#5. 默认值约束(DEFAULT))
- [6. 检查约束(CHECK)](#6. 检查约束(CHECK))
- [7. 外键约束(FOREIGN KEY)](#7. 外键约束(FOREIGN KEY))
约束
约束的作用
约束是为了保证数据的完整性,数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是应防止数据库中存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
数据的完整性要从以下四个方面考虑:
实体完整性(Entity Integrity):例如,同一个表中,不能存在两条完全相同无法区分的记录
域完整性(Domain Integrity):例如,年龄范围0-120,性别范围"男/女"
引用完整性(Referential Integrity):例如,员工所在部门,在部门表中要能找到这个部门
用户自定义完整性(User-defined Integrity):例如,用户名唯一、密码不能为空等,本部门经理的工资不得高于本部门职工的平均工资的5倍
可以在创建表时规定约束(通过create table语句),或者在表创建之后也可以添加约束(通过alter table语句)。
约束的类型
键约束:主键约束、外键约束、唯一键约束
Not NULL约束:非空约束
Check约束:检查约束
Default约束:默认值约束
自增是键约束字段的一个额外的属性
表级约束和列级约束
表级约束:不仅要看约束字段当前单元格的数据,还要看其他单元格的数据。
键约束和检查约束是表级约束。
列级约束:约束字段只看当前单元格的数据即可,和其他单元格无关。
非空约束和默认值约束是列级约束。
所有的表级约束都可以在"information_schema.table_constraints"表中查看。
python
select * from information_schema.table_constraints where table_name = '表名';
约束和索引
在MySQL中键约束会自动创建索引,提高查询效率。
约束和索引不同:
约束是一个逻辑概念,不会占用物理空间
索引是一个物理概念,会占用物理空间
例如,字典里面有要求,不能有重复的字(字一样,读音也一样),这是约束。字典里面有"目录",它可以快速的查找某个字,目录需要占用单独的页,这是索引。
非空约束
not null:限定某个字段/某列的值不允许为空。
只能某个列单独限定非空,不能组合非空
一个表可以有很多列都分别限定了非空
指定非空约束
1)建表时
python
create table 表名(
字段名 数据类型 not null,
字段名 数据类型 not null
);
2)建表后
python
alter table 表名 modify 字段名 数据类型 not null;
-- 如果该字段已经有值了,给该字段增加非空约束,要求该字段的值不能有NULL值
-- 否则需要先处理NULL值才能加上非空约束
删除非空约束
python
alter table 表名 modify 字段名 数据类型;
-- 如果使用modify修改有非空约束的字段时想要保留非空约束,必须带上not null
-- 否则会在修改时,把非空约束丢掉
示例

若是此时依然强行插入,则会报这个错误

插值方式和之前依然是一样
python
insert into not_null_demo(id,name) values(1,'张三');
insert into not_null_demo values(4,'赵六',null);
python
-- 给birthday字段添加非空约束
alter table not_null_demo modify column birthday date not null;
失败的原因是因为birthday字段现在有NULL值,添加不上非空约束

删除表,重新修改便能成功

或者将其null填上一个值,然后再进行修改

python
-- 删除birthday字段的非空约束
alter table not_null_demo modify column birthday date;
desc not_null_demo;

唯一性约束
unique key:用来限制某列的值或几个字段的值组合不能重复。
一个表可以有很多个唯一键约束
每一个唯一键约束字段都会自动创建索引
唯一键约束允许为空
唯一键约束也可以是复合唯一
删除唯一键约束的索引来删除唯一键约束
索引名默认是字段名,复合唯一默认是第一个字段名
指定唯一键约束
1)建表时
python
create table 表名(
字段名 数据类型 unique key,
字段名 数据类型 unique key
);
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
unique key(字段名1),
unique key(字段名2)
);
2)建表后

其实和之前有些像

复合唯一
-- 多个字段的组合是唯一
python
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型,
unique key(字段名1,字段名2,字段名3)
);
示例
python
create table unique_key(
name char(4),
gender char(1),
idcard int,
unique key(name,idcard)
)
多个共同构成唯一键约束的时候,使用第一个作为索引,如下

添加数据,第二句可以插入,但是第三段不能插入,主键字段相同不能插入,但是若是为空,则可以插入
python
insert into unique_key VALUES('孙晓明','男','12345');
insert into unique_key(name) VALUES('孙晓明');
insert into unique_key(name,idcard) VALUES('孙晓明','12345')
查看唯一键约束
python
desc 表名;
show create table 表名;
show index from 表名; -- 查看表的索引信息
select * from information_schema.table_constraints where table_name = '表名';
对于某一个字段可以重复添加唯一键约束,不过没意义

删除唯一键约束
删除表结构中的某一个字段是这样的

python
-- 删除唯一键约束需要手动删除对应的索引,多了一个index
alter table 表名 drop index 索引名;
主键约束
primary key:用来唯一的确定一条记录
唯一并且非空
一个表最多只能有一个主键约束
如果主键是由多列组成,可以使用复合主键
主键列会自动创建索引(能够根据主键查询的,就根据主键查询,效率更高),mysql会给每个表的主键列创建索引,会开辟单独的物理空间来存储每一个主键的目录表(Btree结构)。这样可以根据主键快速查询到某一行的记录
如果删除主键约束了,主键约束对应的索引就自动删除了
唯一键约束和主键约束区别
唯一键约束一个表可以有好几个,但是主键约束只有一个。
唯一键约束本身不带非空限制,如果需要非空,需要单独定义。主键约束不用再定义not null,自身就带非空限制。
指定主键约束
1)建表时
python
create table 表名(
字段名 数据类型 primary key,
字段名 数据类型
);
-- 或
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
primary key(字段名1)
);
2)建表后
python
alter table 表名 add primary key(字段列表);

复合主键
python
create table 表名(
字段名1 数据类型,
字段名2 数据类型,
字段名3 数据类型,
primary key(字段名1,字段名2)
);

删除主键约束
这里和删除唯一键约束不同,因为唯一键约束可能有多个,而主键约束只有一个
python
alter table 表名 drop primary key;

自增约束
auto_increment:给某个字段自动赋值,这个值如无干扰每次+1。
一个表只能有一个自增字段,因为一个表只有一个AUTO_INCREMENT属性记录自增字段值
自增字段只能是key字段,即定义了主键、唯一键等键约束的字段
自增字段应该是数值类型,一般都是整数类型
如果自增列指定了 0 和 null,会在当前最大值的基础上自增,如果自增列手动指定了具体值,直接赋值为具体值
如果手动修改AUTO_INCREMENT属性值, 必须 > 当前自增字段的最大值
指定自增约束
1)建表时
python
create table 表名(
字段名 数据类型 primary key auto_increment,
字段名 数据类型 unique key not null
);
2)建表后
python
alter table 表名 modify 字段名 数据类型 auto_increment;
删除自增约束
python
alter table 表名 modify 字段名 数据类型;
示例
下面这个代码不对,auto_increment必须放在主键、唯一键后面,
python
drop table if exists emp;
create table emp(
eid int auto_increment,
ename varchar(20)
);
所以需要修改为下面这种
python
create table emp(
eid int primary key auto_increment,
ename varchar(20)
);
即使不进行赋值也是会有值
python
insert into emp(ename) values('李四'); -- 不给自增字段指定值,也是自增
insert into emp values(null,'张三'); -- 给自增字段赋值NULL,也是自增

当手动给自增字段赋值时
如果这个值大于当前 AUTO_INCREMENT 属性记录的自增值时,会修改 AUTO_INCREMENT 属性值,下次就从这个值基础上自增
如果这个值小于当前 AUTO_INCREMENT 属性记录的自增值时,不会修改 AUTO_INCREMENT 属性值,
建议不要随意修改 AUTO_INCREMENT 的值,让他自动维护
python
insert into emp values(9, '田七');
insert into emp values(-5,'王五');

默认值约束
default:给某个字段/某列指定默认值,当添加时或修改时,可以使用默认值。
指定默认值约束
python
1)建表时
create table 表名(
字段名 数据类型 primary key,
字段名 数据类型 unique key not null,
字段名 数据类型 unique key,
字段名 数据类型 not null default 默认值
);
-- 默认值约束一般不在唯一键和主键列上加
2)建表后
python
alter table 表名 modify 字段名 数据类型 default 默认值;
删除默认值约束
python
alter table 表名 modify 字段名 数据类型;
-- 如果使用modify修改有默认值约束的字段时想要保留默认值约束,必须带上default 默认值
-- 否则会在修改时,把默认值约束丢掉
示例
python
drop table if exists emp;
create table emp(
eid int primary key,
ename varchar(20) not null,
gender enum('男','女') default '男' not null, -- 非空并且有默认值
address varchar(100) default '不详' -- 可以指定为null
);
insert into emp values(2,'李四',default,default);
insert into emp values(3,'王五',default,null);
select * from emp;
-- 删除emp表的address的默认值约束
alter table emp modify column address varchar(100);
desc emp;
-- 给emp表address增加 不详 默认值
alter table emp modify column address varchar(100) default '不详';
desc emp;
检查约束
check:检查约束用于限制字段中的值的范围。如果对单个字段定义检查约束,那么该字段只允许特定范围的值。如果对一个表定义检查约束,那么此约束会基于行中其他字段的值在特定的字段中对值进行限制。
指定检查约束
1)建表时
python
create table 表名(
字段名 数据类型 check(条件), -- 在字段后面直接加检查约束
字段名 数据类型,
字段名 数据类型,
check (条件) enforced -- 可以限定两个字段之间的取值条件
);
-- 如果省略或指定为enforced,则会强制执行约束,不满足约束的数据行不能插入成功
-- 如果写not enforced,则不满足检查约束也没关系
2)建表后
python
alter table 表名 add check(条件);
alter table 表名 modify 字段名 字段类型 check(条件);
删除检查约束
python
alter table 表名 drop check 检查约束名;
示例
python
drop table if exists emp;
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
age int,
birthday date not null, -- 出生日期
hiredate date not null, -- 入职日期
check(year(hiredate)-year(birthday)>=18) -- 入职时>=18岁
);
年龄为8的时候不能插入,只有>=18才能

使用这种方式不行 alter table emp modify age int; 还是需要使用alter table emp drop check emp_chk_1;
python
alter table emp drop check emp_chk_1;
外键约束
foreign key:限定某个表的某个字段的引用完整性,比如:员工表的员工所在部门的选择,必须在部门表能找到对应的部分。
外键约束会影响性能,效率,所以很多人不愿意加外键约束。
1)建和不建外键约束有什么区别
建外键约束,操作(创建表、删除表、添加、修改、删除)会受到限制,从语法层面受到限制。例如在员工表中不可能添加一个员工信息,它的部门的值在部门表中找不到。
不建外键约束,操作(创建表、删除表、添加、修改、删除)不受限制,要保证数据的引用完整性,只能依靠程序员的自觉,或者是在程序中进行限定。例如在员工表中,可以添加一个员工的信息,它的部门指定为一个完全不存在的部门。
2)主表和从表/父表和子表
主表(父表):被引用的表,被参考的表
从表(子表):引用别人的表,参考别人的表
例如员工表与部门表。员工表的员工所在部门这个字段的值要参考部门表,部门表是主表,员工表是从表。
例如学生表、课程表、选课表。选课表的学生和课程要分别参考学生表和课程表,学生表和课程表是主表,选课表是从表。
3)外键约束的特点
在从表中指定外键约束,并且一个表可以建立多个外键约束
创建表时就指定外键约束的话,先创建主表,再创建从表
删表时,先删从表(或先删除外键约束),再删除主表。或者先解除关系,再各自删除
从表的外键列,必须引用/参考主表的键列(主键或唯一键),因为被依赖/被参考的值必须是唯一的
从表的外键列的数据类型,要与主表被参考/被引用的列的数据类型一致,并且逻辑意义一致。例如都是表示部门编号,都是int类型
外键列也会自动建立索引(根据外键查询效率更快)
外键约束删除后索引不会自动删除,如果要删除对应的索引,必须手动删除
指定外键约束
1)建表时
python
create table 主表(
字段1 数据类型 primary key,
字段2 数据类型
);
create table 从表(
字段1 数据类型 primary key,
字段2 数据类型,
foreign key (从表的某个字段) references 主表(被参考字段)
);
-- (从表的某个字段)的数据类型必须与主表(被参考字段)的数据类型一致,逻辑意义也一样
-- (从表的某个字段)的字段名可以与主表(被参考字段)的字段名不同
2)建表后
alter table 从表 add foreign key (从表的字段) references 主表(被引用字段) [on update xx] [on delete xx];
查看外键约束
python
desc 从表; -- 可以看到外键约束,但看不到外键约束名
show create table 从表; -- 可以看到外键约束名
select * from information_schema.table_constraints where table_name='表名';
-- information_schema数据库名(系统库)
-- table_constraints表名(专门存储各个表的约束)
查看外键字段索引
show index from 表名;
删除外键约束
python
-- 先查看约束名和删除外键约束
select * from information_schema.table_constraints where table_name='表名';
alter table 从表 drop foreign key 外键约束名;
-- 查看索引名和删除索引
show index from 表名;
alter table 从表 drop index 索引名;
示例
python
drop table if exists dept;
create table dept(
did int primary key auto_increment,
dname varchar(50) unique key not null
);
drop table if exists emp;
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
departmentid int,
-- 外键约束只能在字段列表下面单独定义,不能在字段后面直接定义
foreign key (departmentid) references dept(did)
);
desc dept;
desc emp;
show create table dept;
show create table emp;
-- 查看系统库的约束表
select * from information_schema.table_constraints where table_name='emp';
-- 添加父表数据,没有影响
insert into dept values(null,'财务'),(null,'教学'),(null,'咨询'),(null,'后勤');
select * from dept;
-- 添加子表数据,有影响,受到约束
insert into emp values(null,'张三',1); -- 成功
insert into emp values(null,'李四',1); -- 成功
insert into emp values(null,'王五',2); -- 成功
insert into emp values(null,'赵六',6); -- 失败
-- departmentid=6在父表dept中找不到对应部门
select * from emp;
-- 修改子表的外键字段的信息,有影响,受到约束
update emp set departmentid = 3 where id=1; -- 成功
update emp set departmentid = 6 where id=3; -- 失败
-- departmentid=6在父表dept中找不到对应部门
select * from emp;
-- 修改父表的被引用字段的值,受约束
update dept set did = 6 where did=1; -- 失败
-- did=1的部门被子表引用
update dept set did = 6 where did=4; -- 成功
-- did=4的部门没有被子表引用
select * from dept;
-- 删除父表的记录,受约束
delete from dept where did=6; -- 成功
-- did=6的部门没有被子表引用
delete from dept where did=1; -- 失败
-- did=1的部门被子表引用
-- 删除子表的数据,不受约束
delete from emp where name='王五';
select * from emp;
-- 删除父表,受约束
drop table dept; -- 失败
-- 删除子表,不受约束
drop table emp;
-- 建表后添加外键约束
create table emp(
id int primary key auto_increment,
name varchar(20) not null,
departmentid int
);
-- 给emp表(子表)增加外键约束
alter table emp add foreign key (departmentid) references dept(did);
-- 查看emp的约束信息
select * from information_schema.table_constraints where table_name='emp';
-- 键约束(主键、唯一键、外键)都会自动创建索引
-- 查看emp表的索引
show index from emp;
-- 主键字段索引名是PRIMARY,删除主键时,会自动删除对应索引
-- 唯一键字段索引名是字段名,删除唯一键时,就是通过删除对应的索引方式来删除唯一键约束
-- 外键字段索引名是字段名,删除外键约束时,不会自动删除外键字段的索引,因为它们的命名不一样
-- 删除emp表的departmentid字段的外键约束
alter table emp drop foreign key emp_ibfk_1;
show index from emp;
-- 删除emp表的departmentid字段的索引
alter table emp drop index departmentid;
show index from emp;
设置外键约束等级
Cascade方式:在父表上update/delete记录时,同步update/delete子表的匹配记录
Set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子表的外键列不能为not null
No action方式:如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
Restrict方式:同no action, 都是立即检查外键约束
如果没有指定等级,就相当于Restrict方式。
python
create table 主表(
字段1 数据类型 primary key,
字段2 数据类型
);
create table 从表(
字段1 数据类型 primary key,
字段2 数据类型,
foreign key (从表的某个字段) references 主表(被参考字段) on updata (Cascade/Set null) on delete (Cascade/Set null)
);
-- (从表的某个字段)的数据类型必须与主表(被参考字段)的数据类型一致,逻辑意义也一样
-- (从表的某个字段)的字段名可以与主表(被参考字段)的字段名不同

总结
一、约束的作用
约束(Constraint)是为了保证数据的完整性(Data Integrity)而设计的。
数据完整性包括精确性 和可靠性,目的是防止数据库中出现不符合业务规则的数据。
数据完整性从四个方面考虑:
- 实体完整性(Entity Integrity):表中不能有完全相同的两条记录(主键实现)
- 域完整性(Domain Integrity):字段值必须在合理范围内(如年龄 0~120)
- 引用完整性(Referential Integrity):外键必须引用主表中已存在的值
- 用户自定义完整性:由用户自己定义的业务规则(如用户名唯一、经理工资不得超过部门平均工资5倍)
可以在创建表时(CREATE TABLE)或建表后(ALTER TABLE)添加约束。
二、约束的分类
1. 按约束类型划分
- 键约束:主键约束(PRIMARY KEY)、唯一键约束(UNIQUE)、外键约束(FOREIGN KEY)
- 非空约束:NOT NULL
- 默认值约束:DEFAULT
- 检查约束:CHECK
- 自增:AUTO_INCREMENT(属于键约束的附加属性,不是独立约束)
2. 按约束级别划分
-
列级约束 :只针对当前字段本身,与其他字段无关
→ 非空约束、默认值约束
-
表级约束 :需要看当前字段和其他字段的关联关系
→ 主键约束、唯一键约束、外键约束、检查约束
所有表级约束都可以在系统表中查看:
sql
SELECT * FROM information_schema.table_constraints
WHERE table_name = '表名';
三、约束与索引的区别(易错点高频区)
| 维度 | 约束(Constraint) | 索引(Index) |
|---|---|---|
| 概念 | 逻辑概念,用于保证数据规则 | 物理概念,用于加快查询速度 |
| 是否占用空间 | 不占用物理空间 | 占用物理空间(B+Tree结构) |
| 目的 | 保证数据完整性 | 提升查询性能 |
| 创建方式 | CREATE TABLE / ALTER TABLE | CREATE INDEX 或自动创建 |
重要说明 :键约束(主键、唯一键、外键)在创建时,MySQL 会自动创建索引。但约束和索引是两个不同的概念。
四、各类约束详细说明
1. 非空约束(NOT NULL)
- 作用:限定字段值不能为空
- 特点:只能是列级约束,不能组合非空
- 语法:
sql
-- 建表时
CREATE TABLE 表名 (
字段名 类型 NOT NULL,
);
-- 建表后
ALTER TABLE 表名 MODIFY 字段名 类型 NOT NULL;
-- 删除非空约束
ALTER TABLE 表名 MODIFY 字段名 类型; -- 注意:不写 NOT NULL 就是删除
易错点:
- 修改字段时,如果不重新写
NOT NULL,原来的非空约束会被去掉。 - 已有 NULL 值的字段无法直接加上非空约束,必须先处理 NULL 值。
2. 唯一性约束(UNIQUE)
- 作用:保证字段值不能重复
- 特点:允许为空,一个表可有多个唯一约束,会自动创建索引
- 支持复合唯一(多个字段组合唯一)
语法:
sql
-- 建表时
CREATE TABLE 表名 (
name VARCHAR(20) UNIQUE,
idcard VARCHAR(18),
UNIQUE KEY uk_name_idcard(name, idcard) -- 复合唯一
);
-- 建表后
ALTER TABLE 表名 ADD UNIQUE KEY 索引名(字段列表);
-- 删除唯一约束(必须删除对应的索引)
ALTER TABLE 表名 DROP INDEX 索引名;
易错点:
- 同一个字段可以重复写多个
UNIQUE KEY,语法不报错,但完全冗余,无意义。 - 复合唯一键的索引名默认为第一个字段名。
- 唯一约束允许 NULL,多个 NULL 不算重复。
3. 主键约束(PRIMARY KEY)
- 作用:唯一确定一条记录(唯一 + 非空)
- 特点:一个表只能有一个主键,可以是单列或多列(复合主键),会自动创建聚簇索引。
语法:
sql
-- 建表时
CREATE TABLE 表名 (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
-- 建表后
ALTER TABLE 表名 ADD PRIMARY KEY(字段列表);
-- 删除主键
ALTER TABLE 表名 DROP PRIMARY KEY;
易错点:
- 主键不能为 NULL,而唯一键允许 NULL。
- 删除主键会自动删除对应的索引。
- 自增字段必须定义在主键或唯一键上。
4. 自增约束(AUTO_INCREMENT)
- 特点:一个表只能有一个自增字段,必须是 Key 列(主键或唯一键)。
- 传入
NULL或0都会触发自增。 - 手动指定具体正整数时,会使用该值,并可能修改自增起点。
易错点:
AUTO_INCREMENT必须跟在PRIMARY KEY或UNIQUE KEY后面,不能单独使用。- 不要随意手动修改自增列的值,容易导致自增混乱。
5. 默认值约束(DEFAULT)
- 作用:当插入或更新时未指定值,则使用默认值。
- 语法:
sql
字段名 类型 DEFAULT 默认值;
ALTER TABLE 表名 MODIFY 字段名 类型 DEFAULT 默认值;
易错点:
- 修改字段时,如果不重新写
DEFAULT,原来的默认值约束会被清除。
6. 检查约束(CHECK)
- 作用:限制字段值的范围或多个字段之间的关系。
- MySQL 8.0.16 版本开始才真正生效。
推荐写法:
sql
CHECK (TIMESTAMPDIFF(YEAR, birthday, hiredate) >= 18)
易错点:
YEAR(hiredate)-YEAR(birthday)>=18不严谨(忽略月份和日期)。- 低版本 MySQL 中 CHECK 约束不生效,只是语法通过。
7. 外键约束(FOREIGN KEY)
- 作用:保证引用完整性(从表的外键必须在主表中存在)。
- 会自动在外键列创建普通索引(显示为
MUL)。
语法:
sql
FOREIGN KEY (dept_id) REFERENCES dept(dept_id)
ON DELETE CASCADE -- 级联删除
ON UPDATE RESTRICT; -- 默认行为
易错点:
- 删除主表前必须先删除从表或先删除外键约束。
- . 删除唯一键 = 删除对应的索引
- 删除外键约束不会自动删除对应的索引 ,需要手动
DROP INDEX。 - 外键会影响
INSERT、UPDATE、DELETE性能,很多公司不推荐在生产环境使用外键,改用程序控制。