1. 约束的定义

1.1 作用

约束(Constraints)​是用于限制表中数据存储规则的机制,目的是保证数据的完整性​(正确性、一致性、有效性),通过约束,可以避免无效或非法数据进入数据库,是数据库设计的核心组成部分。颜色

1.2 范围

约束是表级的强制规定。
可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后通过 ALTER TABLE 语句规定约束。

1.3 查看已有表约束

SELECT * FROM information_schema.table_constraints
WHERE table_name = '表名称';
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)

2. 约束的分类

2.1 根据约束的列数

  • 单列约束:每个约束只约束一列, 同下述列级约束相似
  • 多列约束:每个约束可约束多列数据,同下述表级约束相似

2.2 根据约束的范围

  • 列级约束:聚焦单列的“局部规则”(如某列不能为空、某列值必须唯一)。只能作用在一个列上,跟在列的定义后面,如:
    常见类型​:
    NOT NULL(非空)
    UNIQUE(唯一)
    CHECK(检查条件)
    DEFAULT(默认值)
    PRIMARY KEY(单列主键)
  • 表级约束:可以作用在多个列上,不与列一起,而是单独定义(列定义完成后单独声明
    常见类型​:
    PRIMARY KEY(多列联合主键)
    FOREIGN KEY(外键,关联其他表)
    UNIQUE(多列联合唯一)

2.3 根据约束作用分类

约束类型 作用 是否自动
生成索引
是否常用
主键约束
PRIMARY KEY
唯一标识一条记录,不允许重复或 NULL 是(自动创建主键索引)
外键约束
PRIMARY KEY
建立表间关联,保证子表数据引用父表的有效数据 否(需手动创建) 不一定
唯一约束
UNIQUE
列中值不能重复,但允许单个 NULL 是(自动创建唯一索引
非空约束
NOT NULL
列中值不能为 NULL
默认约束
DEFAULT
字段无显式值时,使用预设的默认值
检查约束
CHECK
列值必须满足指定条件(如范围、格式) 否(MySQL 8.0+ 强制) 不一定

3. 六大约束

3.1 非空约束 - NOT NULL

3.1.1 作用:

限定某个字段/某列的值不允许为空(保证列中值不能为 NULL),用于必须填写的关键字段(如用户名、身份证号)

3.1.2 特点:

  • 默认所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型
  • 非空空字符串’'不等于NULL,0也不等于NULL。

3.1.3 添加约束

一、建表时添加
CREATE TABLE 表名称(
字段名 数据类型,
字段名 数据类型 NOT NULL,
字段名 数据类型 NOT NULL
);

二、建表后添加

alter table 表名称 modify 字段名 数据类型 not null;

(4) 删除约束

#去掉not null,相当于修改某个非注解字段,该字段允许为空
alter table 表名称 modify 字段名 数据类型 NULL;alter table 表名称 modify 字段名 数据类型; #

3.2 唯一性约束-UNIQUE

3.2.1 作用:

用来限制某个字段/某列的值不能重复。

3.2.2 特点:

  • 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。
  • 唯一性约束允许列值为空。
  • 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
  • MySQL会给唯一约束的列上默认创建一个唯一索引

3.2.3 复合唯一约束

复合唯一约束(Composite Unique Constraint)是唯一约束的扩展形式,用于保证多列组合的值不重复​(允许单列重复,但多列组合必须唯一)

语法1:表级约束(显式命名,推荐)​​
通过 CONSTRAINT 显式命名约束,并指定多列组合:

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    phone VARCHAR(20) NOT NULL,
    department VARCHAR(50),
    -- 复合唯一约束:username 和 phone 组合必须唯一(显式命名)
    CONSTRAINT uk_emp_username_phone UNIQUE (username, phone)
);

语法2:隐式约束(未显式命名)​​
直接在表定义末尾用 UNIQUE 声明多列,MySQL 会自动生成约束名(格式通常为 表名_列1_列2_uk):

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    phone VARCHAR(20) NOT NULL,
    department VARCHAR(50),
    -- 隐式复合唯一约束(自动生成约束名)
    UNIQUE (username, phone)
);

3.2.4 添加唯一约束

一、建表时

create table 表名称(
字段名 数据类型,
字段名 数据类型 unique,
字段名 数据类型 unique key,
字段名 数据类型
);
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
[constraint 约束名] unique key(字段列表)#字段列表中写的是多个字段名,多个字段名用逗号分隔,表示那么是复合唯一,即多
);-- 使用表级约束语法

二、建表后指定唯一键约束

#字段列表中如果是一个字段,表示该列的值唯一。如果是两个或更多个字段,那么复合唯一,即多个字段的组合是唯一的
#方式1:
alter table 表名称 add unique key(字段列表);
#方式2:
alter table 表名称 modify 字段名 字段类型 unique;

3.2.5 删除唯一约束

  • 添加唯一性约束的列上也会自动创建唯一索引。
  • 删除唯一约束只能通过删除唯一索引的方式删除
  • 删除时需要指定唯一索引名,唯一索引名就和唯一约束名一样。
  • 如果创建唯一约束时未指定名称,如果是单列,就默认和列名相同;如果是组合列,那么默认和()中排在第一个的列名相同。也可以自定义唯一性约束名。

方式一、1. 通过约束名删除(推荐)​​
如果创建唯一约束时显式指定了约束名(推荐做法),可以通过 ALTER TABLE DROP CONSTRAINT 语句删除约束,此时 MySQL 会自动删除对应的唯一索引。

-- 语法:
ALTER TABLE 表名 DROP CONSTRAINT 约束名;

方式二、 通过索引名删除(隐式约束)​​
若添加约束时未显式命名(MySQL 自动生成约束名),需先查询约束对应的索引名,再通过索引名删除。

-- 语法:
ALTER TABLE 表名 DROP INDEX 索引名;

3.3 主键约束 – PRIMARY KEY

3.3.1 作用

唯一标识表中的每一行记录,确保数据的唯一性和非空性。主键是表的“逻辑标识”,用于快速定位和关联数据。

3.3.2 特点

  1. 主键约束相当于唯一约束+非空约束的组合,主键约束列不允许重复,也不允许出现空值。
  2. 一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建。
  3. 主键约束对应着表中的一列或者多列(复合主键)。 如果是多列组合的复合主键约束,那么这些列都不允许为空值
  4. MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
  5. 与索引的关系:当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引。如果删除主键约束了,主键约束对应的索引就自动删除了。(能够根据主键查询的,就根据主键查询,效率更高)

3.3.3 添加主键约束

(1)建表时指定主键约束

create table 表名称(
字段名 数据类型 primary key, #列级模式
字段名 数据类型,
字段名 数据类型
);
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
[constraint 约束名] primary key(字段名) #表级模式
);

举例

CREATE TABLE emp(
id INT PRIMARY KEY AUTO_INCREMENT ,
NAME VARCHAR(20)
);--列级
CREATE TABLE emp5(
id INT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(20),
pwd VARCHAR(15),
CONSTRAINT emp5_id_pk PRIMARY KEY(id)--表级
);

(2)建表后增加主键约束

ALTER TABLE 表名称 ADD PRIMARY KEY(字段列表);
 #字段列表可以是一个字段,也可以是多个字段,如果是多个字段的话,是复合主键
 #举例
 ALTER TABLE student ADD PRIMARY KEY (sid);

3.3.4 复合主键

语法:

create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
primary key(字段名1,字段名2) #表示字段1和字段2的组合是唯一的,也可以有更多个字段
);
#举例 --选课表
create table student_course(
sid int,
cid int,
score int,
primary key(student_id,course_id) #复合主键
);                                                       

3.4 外键约束 FOREIGN KEY

(实际工作中已不推荐使用,了解即可)

3.4.1 作用

建立表之间的关联关系,保证数据的参照完整性​(Referential Integrity)。子表的外键值必须指向父表中已存在的主键值(或为 NULL,若外键允许 NULL)。(通常用于一对多关系​(如“用户表”与“订单表”:一个用户可对应多个订单))

3.4.2 特点

  • 创建时:
    • 创建(CREATE)表时就指定外键约束的话,先创建主表,再创建从表
    • 在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如student_ibfk_1;),也可以指定外键约束名。
  • 删除时:
    • 删表时,先删从表(或先删除外键约束),再删除主表
    • 当主表的记录被从表参照时,主表的记录将不允许删除,如果要删除数据,需要先删除从表中依赖该记录的数据,然后才可以删除主表的数据
  • 字段类型:
    子从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致。如果类型不一样,创建子表时,就会出现错误“ERROR 1005 (HY000): Can’t create table’database.tablename’(errno: 150)”。
  • 支持级联操作(Cascade):当父表数据变更时,子表数据可自动更新或删除(如 ON DELETE CASCADE)。
  • 和索引的关系:当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。但是索引名是外键的约束名。(根据外键查询效率很高)只是删除外键约束后,必须 手动 删除对应的索引

3.4.3 添加外键约束

create table 从表名称(
字段1 数据类型 primary key,
字段2 数据类型,
[CONSTRAINT <外键约束名称>] FOREIGN KEY(从表的某个字段) references 主表名(被参考字段)
);
#(从表的某个字段)的数据类型必须与主表名(被参考字段)的数据类型一致,逻辑意义也一样
#(从表的某个字段)的字段名可以与主表名(被参考字段)的字段名一样,也可以不一样
-- FOREIGN KEY: 在表级指定子表中的列
-- REFERENCES: 标示在父表中的列

举例

create table emp(#从表
eid int primary key, #员工编号
ename varchar(5), #员工姓名
deptid int, #员工所在的部门
foreign key (deptid) references dept(dep_id) #在从表中指定外键约束
#emp表的deptid和和dept表的dep_id的数据类型一致,意义都是表示部门的编号
);

(2)建表后

ALTER TABLE 从表名 ADD [CONSTRAINT 约束名] FOREIGN KEY (从表的字段) REFERENCES 主表名(被引用字段) [on update xx][on delete xx];

举例

ALTER TABLE emp1
ADD [CONSTRAINT emp_dept_id_fk] FOREIGN KEY(dept_id) REFERENCES dept(dept_id);

3.4.4 外键对主表和从表的影响

  • 添加了外键约束后,主表的修改和删除数据受约束
  • 添加了外键约束后,从表的添加和修改数据受约束
  • 在从表上建立外键,要求主表必须存在
  • 删除主表时,要求从表从表先删除,或将从表中外键引用该主表的关系先删除

3.4.5 约束等级

  • Cascade方式 :在父表上update/delete记录时,同步update/delete掉子表的匹配记录
  • Set null方式 :在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子
  • 表的外键列不能为not null No action方式 :如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
  • Restrict方式 :同no action, 都是立即检查外键约束
  • Set default方式 (在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别

如果没有指定等级,就相当于Restrict方式。 对于外键约束,最好是采用: ON UPDATE CASCADE ON DELETE RESTRICT 的方式。

3.4.6 删除外键约束

(1)第一步先查看约束名和删除外键约束

SELECT * FROM information_schema.table_constraints WHERE table_name = '表名称';#查看某个表的约束名
ALTER TABLE 从表名 DROP FOREIGN KEY 外键约束名;

(2)第二步查看索引名和删除索引。(注意,只能手动删除)

SHOW INDEX FROM 表名称; #查看某个表的索引名
ALTER TABLE 从表名 DROP INDEX 索引名;

举例

SELECT * FROM information_schema.table_constraints WHERE table_name = 'emp';
alter table emp drop foreign key emp_ibfk_1;
show index from emp;
alter table emp drop index deptid;

3.4.6 开发规范

成本问题:在 MySQL 里,外键约束是有成本的,需要消耗系统资源。对于大并发的 SQL 操作,有可能会不适合。比如大型网站的中央数据库,可能会 因为外键约束的系统开销而变得非常慢 。所以, MySQL 允许你不使用系统自带的外键约束,在 应用层面 完成检查数据一致性的逻辑。也就是说,即使你不用外键约束,也要想办法通过应用层面的附加逻辑,来实现外键约束的功能,确保数据的一致性。
阿里开发规范
【 强制 】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于 单机低并发不适合 分布式 、 高并发集群 ;级联更新是强阻塞,存在数据库 更新风暴 的风险;外键影响数据库的 插入速度

3.5 默认约束-DEFAULT

3.5.1 作用

作用为列指定默认值,当插入数据时未显式提供该列的值时,自动使用默认值。

3.5.2 特点

  • 默认值可以是字面量(如 ‘未知’)、函数(如 CURRENT_TIMESTAMP)或表达式。
  • 适用于需要填充基础值的场景(如创建时间、状态标记)。

3.5.3 添加默认值

一、建表前

create table 表名称(
再举例:
字段名 数据类型 default 默认值 ,
字段名 数据类型 not null default 默认值,
字段名 数据类型 not null default 默认值,
primary key(字段名),
unique key(字段名)
);

#说明:默认值约束一般不在唯一键和主键列上加
二、建表后

alter table 表名称 modify 字段名 数据类型 default 默认值;
#如果这个字段原来有非空约束,你还保留非空约束,那么在加默认值约束时,还得保留非空约束,否则非空约束就被 删除了
#同理,在给某个字段加非空约束也一样,如果这个字段原来有默认值约束,你想保留,也要在modify语句中保留默认值约束,否则就删除了
alter table 表名称 modify 字段名 数据类型 default 默认值 not null
--alter table employee modify tel char(11) default '' not null;#给tel字段增加默认值约束,并
保留非空约束

3.5.3 删除默认值约束

#删除默认值约束,也不保留非空约束
alter table 表名称 modify 字段名 数据类型 ;
#删除默认值约束,保留非空约束
alter table 表名称 modify 字段名 数据类型 not null;
--alter table employee modify gender char; #删除gender字段默认值约束,如果有非空约束,也一并删除
--alter table employee modify tel char(11) not null;#删除tel字段默认值约束,保留非空约束

3.6 检查约束-check

3.6.1 作用

限制列中的值必须满足特定条件​(如数值范围、枚举值等)。MySQL 8.0.16 及以上版本强制生效(早期版本即使写了可能不会报错,但也不会生效)

3.6.2 特点

  • 条件表达式需返回 TRUE 或 FALSE。
  • 支持复杂逻辑(如 age > 0 AND age < 150)。

3.6.3 添加检查约束

一、创建表时

# 列级约束(直接附加在字段定义后)
CREATE TABLE 表名 (
    列名 数据类型 CHECK (布尔表达式)
);
# 表级约束(独立声明,支持多字段校验)
CREATE TABLE 表名 (1 数据类型,2 数据类型,
    CONSTRAINT 约束名 CHECK (布尔表达式)
);

二、通过修改新增

# 新增 CHECK 约束
ALTER TABLE 表名
ADD CONSTRAINT 约束名 CHECK (布尔表达式);

3.6.4 删除检查约束

ALTER TABLE 表名
ADD CONSTRAINT 约束名 CHECK (布尔表达式);

3.6.5 删除约束

ALTER TABLE 表名
DROP CONSTRAINT 约束名;  -- SQL Server/PostgreSQL

ALTER TABLE 表名
DROP CHECK 约束名;      -- MySQL

3.7 自增属性-AUTO_INCREMENT

3.7.1 作用

AUTO_INCREMENT​:是列的属性(不算约束),用于定义列的自动生成逻辑(插入时自动生成下一个整数值),本身不直接限制数据取值,而是辅助实现约束(如主键的唯一性)。(当需要产生唯一标识符或顺序值时,可设置自增长)

3.7.1 特点

(1)一个表最多只能有一个自增长列
(2)自增长列约束的列必须是键列(主键列,唯一键列)
(3)自增约束的列的数据类型必须是整数类型

如果插入时显式将 AUTO_INCREMENT 列设为 0 或 NULL,MySQL 会根据 sql_mode 中的 > NO_AUTO_VALUE_ON_ZERO 模式决定行为(默认启用此模式):
NO_AUTO_VALUE_ON_ZERO 模式(默认)​​: 插入 0 或 NULL 会被视为“未指定自增值”,数据库会忽略该值,直接使用当前最大值 + 1​ 作为新值(与未显式指定值的行为一致)。
AUTO_INCREMENT 模式(已废弃)​​: 插入 0 会被视为 1(此模式在 MySQL 8.0+ 中已废弃,无需关注)。

3.7.2 添加自增属性

一、建表时添加

create table 表名称(
字段名 数据类型 primary key auto_increment,
字段名 数据类型 unique key not null,
字段名 数据类型 unique key,
字段名 数据类型 not null default 默认值,
);
create table 表名称(
字段名 数据类型 default 默认值 ,
字段名 数据类型 unique key auto_increment,
字段名 数据类型 not null default 默认值,,
primary key(字段名)
);

二、修改表添加

alter table 表名称 modify 字段名 数据类型 auto_increment;

3.7.3 删除属性

#alter table 表名称 modify 字段名 数据类型 auto_increment;#给这个字段增加自增约束
alter table 表名称 modify 字段名 数据类型; #去掉auto_increment相当于删除

3.7.4 自增变量的持久化

在MySQL 8.0之前,自增主键AUTO_INCREMENT的值如果大于max(primary key)+1,在MySQL重启后,会重置AUTO_INCREMENT=max(primary key)+1,这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。新版本已经支持持久化了。

4 常见面试题

面试1、为什么建表时,加 not null default ‘’ 或 default 0

答:不想让表中出现null值。

面试2、为什么不想要 null 的值

答:(1)不好比较。null是一种特殊值,比较时只能用专门的is null 和 is not null来比较。碰到运算符,通常返回null。
(2)效率不高。影响提高索引效果。因此,我们往往在建表时 not null default ‘’ 或 default 0

面试3、带AUTO_INCREMENT约束的字段值是从1开始的吗?

在MySQL中,默认AUTO_INCREMENT的初始值是1,每新增一条记录,字段值自动加1。设置自增属性(AUTO_INCREMENT)的时候,还可以指定第一条插入记录的自增字段的值,这样新插入的记录的自增字段值从初始值开始递增,如在表中插入第一条记录,同时指定id值为5,则以后插入的记录的id值就会从6开始往上增加。添加主键约束时,往往需要设置字段自动增加属性。

面试4、并不是每个表都可以任意选择存储引擎? 外键约束(FOREIGN KEY)不能跨引擎使用。

MySQL支持多种存储引擎,每一个表都可以指定一个不同的存储引擎,需要注意的是:外键约束是用来保证数据的参照完整性的,如果表之间需要关联外键,却指定了不同的存储引擎,那么这些表之间是不能创建外键约束的。所以说,存储引擎的选择也不完全是随意的

面试5. 约束和索引有什么区别?

​约束​:是数据库层的规则(如 NOT NULL、UNIQUE),用于限制数据的合法性,不直接优化查询性能(但唯一约束会自动创建索引)。
​索引​:是物理存储结构(如 B+ 树),用于加速查询(如 WHERE、JOIN、ORDER BY),但不限制数据规则(允许重复值)。
​关联​:唯一约束会自动创建唯一索引(加速唯一查询),但普通索引不影响约束规则。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐