约束
约 1090 字大约 4 分钟
布欧-Lewyon
2026-05-15
首页 › MySQL › 数据类型与约束(在新窗口打开) › 约束
约束(Constraint)是定义在表上的规则,用于保证数据的完整性和一致性。MySQL 支持六类约束,覆盖行级、表级和引用完整性。
约束分类
| 约束 | 关键字 | 说明 |
|---|---|---|
| 主键约束 | PRIMARY KEY | 唯一标识一行,非空且唯一 |
| 唯一约束 | UNIQUE | 值不可重复,可空 |
| 非空约束 | NOT NULL | 字段不可为 NULL |
| 默认约束 | DEFAULT | 未显式赋值时用默认值 |
| 检查约束 | CHECK | MySQL 8.0.16+ 有效 |
| 外键约束 | FOREIGN KEY | 引用另一张表的完整性 |
主键约束(PRIMARY KEY)
每个表应有一个主键,作为行的唯一标识:
CREATE TABLE t1 (
id INT AUTO_INCREMENT PRIMARY KEY, -- 列级定义
name VARCHAR(50)
);
-- 等价于表级定义
CREATE TABLE t2 (
id INT AUTO_INCREMENT,
name VARCHAR(50),
PRIMARY KEY (id)
);
-- 复合主键(多列组合唯一)
CREATE TABLE t3 (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id)
);原则:主键字段不应有业务含义,建议用自增
INT或BIGINT、UUID 排序版(如 ULID)。
唯一约束(UNIQUE)
保证列或列组合的值不重复,允许有一个 NULL 值(MySQL 中多个 NULL 不算重复):
CREATE TABLE user (
id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE, -- 单列唯一
phone VARCHAR(20)
);
-- 复合唯一:同一用户不能在同一门课中重复注册
CREATE TABLE enrollment (
user_id INT,
course_id INT,
enrolled_at DATETIME,
UNIQUE KEY uk_user_course (user_id, course_id)
);非空约束(NOT NULL)
CREATE TABLE product (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL, -- 名称必填
price DECIMAL(10, 2) NOT NULL, -- 价格必填
desc TEXT -- 描述可空
);如果一个字段同时有 UNIQUE 和 NOT NULL,其语义类似主键,但一个表可以有多个这样的组合。
默认约束(DEFAULT)
CREATE TABLE order_ (
id INT PRIMARY KEY,
status VARCHAR(20) DEFAULT 'PENDING', -- 默认待支付
amount DECIMAL(10, 2) DEFAULT 0.00,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO order_ (id) VALUES (1); -- status 和 amount 自动填充默认值
SELECT * FROM order_;
-- id: 1, status: PENDING, amount: 0.00, created_at: (当前时间)
DEFAULT也可以使用表达式(MySQL 8.0.13+),如DEFAULT (UUID_TO_BIN(UUID()))。
检查约束(CHECK)
MySQL 8.0.16 之前 CHECK 语法被解析但忽略。8.0.16+ 真正生效:
CREATE TABLE employee (
id INT PRIMARY KEY,
age INT CHECK (age >= 18 AND age <= 65), -- 年龄限制
salary DECIMAL(10, 2) CHECK (salary >= 0)
);
-- 插入非法数据会报错
INSERT INTO employee VALUES (1, 16, 5000); -- ERROR: Check constraint 'employee_chk_1' is violated-- 命名约束(便于后续删除或修改)
CREATE TABLE product2 (
price DECIMAL(10, 2),
CONSTRAINT price_positive CHECK (price >= 0)
);外键约束(FOREIGN KEY)
保证子表的值必须在父表的主键或唯一键中存在:
CREATE TABLE department (
id INT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(50),
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES department(id)
ON DELETE CASCADE -- 父表删除时,子表对应行自动删除
ON UPDATE CASCADE -- 父表主键更新时,子表外键同步更新
);外键的引用动作
| 动作 | 说明 |
|---|---|
RESTRICT(默认) | 有子记录时禁止删除/更新父记录 |
CASCADE | 级联删除/更新子记录 |
SET NULL | 父记录删除后,子表外键设为 NULL |
NO ACTION | 与 RESTRICT 同(SQL 标准术语) |
外键的实际使用争议
MySQL 外键的优缺点:
| 优点 | 缺点 |
|---|---|
| 保证引用完整性,避免脏数据 | 写操作时额外校验,影响性能 |
| 级联操作简化代码 | 分库分表时无法跨库使用 |
| 关系清晰,自文档化 | 高并发写入时会产生锁竞争 |
生产实践:金融、财务等强一致性场景用外键保障完整性;互联网高并发业务通常由应用层保证关联关系,MySQL 层面不设外键提升写入性能。
组合使用
CREATE TABLE student (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
student_no CHAR(10) UNIQUE NOT NULL,
gender ENUM('M', 'F') NOT NULL DEFAULT 'M',
birth_date DATE,
class_id INT,
CONSTRAINT fk_class FOREIGN KEY (class_id) REFERENCES class(id)
);小结
- 约束确保数据完整性:
PRIMARY KEY唯一标识行、UNIQUE防重复、NOT NULL防空、DEFAULT定默认值、CHECK做条件校验、FOREIGN KEY做引用约束。 - 复合约束(复合主键、复合唯一键)用表级定义。
- 外键 8.0.16+ 的
CHECK真正生效,但在互联网高频场景鲜有使用;CHECK适合做简单的字段范围校验。 - 实际线上环境,外键的取舍取决于业务对一致性与性能的权衡。
