以下是 MySQL 表的约束(Constraints) 一文详解,适合作为“第六节”的核心内容,内容覆盖 MySQL 8.0+ 主流用法,包含原理、语法、常见场景、注意事项和面试/生产高频考点。
一、MySQL 表约束总览(2025–2026 主流)
| 约束类型 | 英文名称 | 作用 | 是否允许 NULL | 是否允许重复 | 是否可以有多列 | 是否可以自定义名称 | 是否可以延迟检查 |
|---|---|---|---|---|---|---|---|
| 主键约束 | PRIMARY KEY | 唯一标识每一行,唯一 + 非空 | 否 | 否 | 可以(复合主键) | 可以 | 否 |
| 唯一约束 | UNIQUE | 列/列组合值唯一 | 可以 | 否 | 可以 | 可以 | 否 |
| 非空约束 | NOT NULL | 该列不允许为空 | 否 | 可以 | — | 不可 | 否 |
| 默认值约束 | DEFAULT | 未指定值时自动填充默认值 | — | — | — | 不可 | 否 |
| 检查约束 | CHECK | 自定义条件校验(MySQL 8.0.16+ 真正生效) | — | — | 可以 | 可以 | 否 |
| 外键约束 | FOREIGN KEY | 维护参照完整性 | 可以 | 可以 | 可以 | 可以 | 可以(延迟) |
二、每种约束详细语法与实战说明
1. 主键约束(PRIMARY KEY)
-- 方式1:列级定义(最常见)
CREATE TABLE users (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL
);
-- 方式2:表级定义(适合复合主键)
CREATE TABLE order_items (
order_id BIGINT NOT NULL,
item_id BIGINT NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (order_id, item_id)
);
-- 方式3:已有表添加主键(必须先满足唯一+非空)
ALTER TABLE users ADD PRIMARY KEY (id);
重要特性:
- 自动创建唯一索引(名为
PRIMARY) - InnoDB 表必须有主键(推荐自增 BIGINT)
- 没有主键的表会隐式创建 6 字节 ROWID 作为聚簇索引(不推荐)
2. 唯一约束(UNIQUE)
-- 列级
CREATE TABLE employees (
emp_no INT PRIMARY KEY,
email VARCHAR(100) UNIQUE NOT NULL
);
-- 表级(复合唯一)
CREATE TABLE user_roles (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
UNIQUE KEY uk_user_role (user_id, role_id)
);
-- 已有表添加
ALTER TABLE employees ADD UNIQUE KEY uk_email (email);
ALTER TABLE user_roles ADD UNIQUE (user_id, role_id);
关键区别 vs 主键:
| 项目 | PRIMARY KEY | UNIQUE |
|---|---|---|
| 允许 NULL | 否 | 是(但 NULL 不算重复) |
| 数量限制 | 一个表只能一个 | 可以多个 |
| 是否自动创建索引 | 是(聚簇索引) | 是(普通唯一索引) |
| 名字 | 固定为 PRIMARY | 可自定义 |
面试常问:一张表可以有多个 UNIQUE 约束,但只能有一个 PRIMARY KEY。
3. 非空约束(NOT NULL)
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
注意:
- 已有表添加 NOT NULL 时,列中不能有 NULL 值,否则报错
ALTER TABLE ... MODIFY COLUMN可以添加/移除 NOT NULL
4. 默认值(DEFAULT)
-- 普通默认值
status TINYINT NOT NULL DEFAULT 0,
-- 表达式默认值(MySQL 8.0.13+)
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- 虚拟列默认值(函数表达式)
full_name VARCHAR(101) GENERATED ALWAYS AS (CONCAT(first_name,' ',last_name)) STORED,
MySQL 8.0+ 支持的 DEFAULT 表达式:
- CURRENT_TIMESTAMP
- NOW()
- UUID()
- (expression) AS …
5. 检查约束(CHECK)—— MySQL 8.0.16+ 真正生效
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
age TINYINT UNSIGNED NOT NULL,
salary DECIMAL(10,2) NOT NULL,
-- 方式1:列级
CHECK (age >= 18 AND age <= 65),
-- 方式2:表级(可跨列)
CONSTRAINT chk_salary CHECK (salary >= 3000 AND salary <= 100000)
);
-- 已有表添加
ALTER TABLE employees
ADD CONSTRAINT chk_age CHECK (age BETWEEN 18 AND 65);
重要提醒:
- MySQL 5.7 及之前版本:CHECK 语法可以写,但不生效(只是语法通过)
- 8.0.16 之后才真正校验
- 性能开销较小,但复杂 CHECK 会影响写入性能
6. 外键约束(FOREIGN KEY)——参照完整性
-- 先创建主表
CREATE TABLE departments (
dept_id INT PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(50) NOT NULL UNIQUE
);
-- 再创建从表
CREATE TABLE employees (
emp_id INT PRIMARY KEY AUTO_INCREMENT,
dept_id INT NOT NULL,
name VARCHAR(50) NOT NULL,
FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
-- 更完整的写法(推荐生产环境)
CONSTRAINT fk_emp_dept
FOREIGN KEY (dept_id)
REFERENCES departments(dept_id)
ON DELETE RESTRICT -- 默认行为
ON UPDATE CASCADE
);
外键动作选项(ON DELETE / ON UPDATE):
| 选项 | 含义 | 典型使用场景 |
|---|---|---|
| RESTRICT | 禁止删除/更新主表记录(默认) | 严格保护数据完整性 |
| CASCADE | 级联删除/更新 | 主从数据必须保持一致(如订单-明细) |
| SET NULL | 主表删除/更新后从表对应字段置 NULL | 可选关联关系 |
| NO ACTION | 同 RESTRICT(MySQL 中等价) | — |
外键使用建议(生产环境):
- 小型项目、性能敏感表 → 可以不建外键,用业务代码保证
- 中大型业务系统、数据质量要求高 → 强烈建议建外键 + 合适的 ON DELETE/UPDATE 策略
- 外键会创建索引(如果从表字段没有索引,会自动创建)
三、约束的增删改查操作总结
-- 查看表所有约束
SHOW CREATE TABLE employees\G
-- 查看所有外键
SELECT * FROM information_schema.REFERENTIAL_CONSTRAINTS
WHERE TABLE_SCHEMA = 'your_db';
-- 删除约束(必须知道约束名)
ALTER TABLE employees DROP FOREIGN KEY fk_emp_dept;
ALTER TABLE employees DROP PRIMARY KEY;
ALTER TABLE employees DROP INDEX uk_email;
ALTER TABLE employees MODIFY COLUMN email VARCHAR(100) NULL; -- 移除 NOT NULL
-- 添加约束(已有数据要满足条件)
ALTER TABLE employees ADD UNIQUE (email);
ALTER TABLE employees ADD CONSTRAINT pk_emp PRIMARY KEY (emp_id);
四、面试/生产高频问题 20 条(建议背熟)
- 主键和唯一约束的本质区别是什么?
- 一张表可以有几个主键?几个唯一约束?
- MySQL 外键一定需要创建索引吗?
- ON DELETE CASCADE 和 ON DELETE SET NULL 的适用场景分别是什么?
- MySQL 什么时候真正开始校验 CHECK 约束?
- 没有主键的 InnoDB 表底层是怎么存储的?
- 复合主键的顺序对查询性能有影响吗?
- 外键约束会影响 insert/update/delete 的性能吗?
- 如何批量删除外键约束?
- DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 的典型使用场景?
希望这份总结能帮你彻底搞懂 MySQL 表的约束体系。
需要我继续补充外键级联实际案例、CHECK 约束性能测试、无主键表 vs 有主键性能对比等更深入的内容吗?