MySQL: MySQL约束与数据完整性详解
最后更新:2026-08-26
约束是数据完整性的守护者——防止脏数据进入数据库。
本课系统讲解所有约束类型及管理。
graph TB
A[MySQL 约束] --> B[PRIMARY KEY<br/>主键约束]
A --> C[FOREIGN KEY<br/>外键约束]
A --> D[UNIQUE<br/>唯一约束]
A --> E[NOT NULL<br/>非空约束]
A --> F[CHECK<br/>检查约束]
C --> C1[CASCADE]
C --> C2[SET NULL]
C --> C3[RESTRICT]
1. 你将学到
- PRIMARY KEY 主键约束
- FOREIGN KEY 外键约束与级联
- UNIQUE 唯一约束
- NOT NULL 非空约束
- CHECK 检查约束(8.0+)
2. 一个真实的故事
(1) 痛点:脏数据无处不在
订单系统运行半年后,数据质量触目惊心:出现了负数金额的订单、没有关联客户的"幽灵订单"、同一个用户名注册了 5 个账号。开发团队试图在应用层校验,但前后端代码分散,总有漏洞。一次促销活动中,黑客绕过前端校验提交了负数金额,直接造成财务损失。
(2) 约束的解法
在数据库层面强制数据完整性——PRIMARY KEY 保证唯一、FOREIGN KEY 保证关联、CHECK 保证范围、UNIQUE 保证不重复。
| 维度 | 应用层校验 | 数据库约束 |
|---|---|---|
| 防护范围 | 仅本应用 | 所有连接来源 |
| 可绕过性 | 前端/接口可绕 | 不可绕过 |
| 维护成本 | 分散在多处 | 集中在表定义 |
| 数据安全 | 中等 | 最高 |
3. 五大约束总览
| 约束 | 作用 | 允许 NULL | 允许重复 |
|---|---|---|---|
| PRIMARY KEY | 唯一标识每行 | ❌ | ❌ |
| FOREIGN KEY | 关联其他表 | ✅ | ✅ |
| UNIQUE | 值不能重复 | ✅ | ❌ |
| NOT NULL | 不能为 NULL | ❌ | ✅ |
| CHECK | 满足条件 | ✅ | ✅ |
4. PRIMARY KEY 主键
▶ 示例:主键约束
SQL
-- 单列主键
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50)
);
-- 复合主键
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);
5. FOREIGN KEY 外键
▶ 示例:外键与级联
SQL
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT,
amount DECIMAL(10,2),
FOREIGN KEY (customer_id) REFERENCES customers(id)
ON DELETE CASCADE -- 删除客户时级联删除订单
ON UPDATE CASCADE -- 更新客户ID时级联更新
);
-- 级联选项
-- CASCADE: 同步删除/更新
-- SET NULL: 设为 NULL
-- RESTRICT: 拒绝操作(默认)
-- NO ACTION: 同 RESTRICT
6. UNIQUE 唯一约束
▶ 示例:唯一约束
SQL
-- 单列唯一
CREATE TABLE users (
id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE,
username VARCHAR(50) UNIQUE
);
-- 复合唯一
CREATE TABLE user_roles (
user_id INT,
role_id INT,
UNIQUE (user_id, role_id)
);
7. NOT NULL 非空约束
▶ 示例:非空约束
SQL
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
description TEXT -- 允许 NULL
);
8. CHECK 检查约束(8.0+)
▶ 示例:CHECK 约束
SQL
CREATE TABLE employees (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
age INT CHECK (age >= 18 AND age <= 65),
salary DECIMAL(10,2) CHECK (salary > 0),
email VARCHAR(100) CHECK (email LIKE '%@%.%')
);
-- 添加 CHECK 约束
ALTER TABLE employees ADD CONSTRAINT chk_age CHECK (age >= 18);
-- 删除 CHECK 约束
ALTER TABLE employees DROP CHECK chk_age;
9. 约束管理
▶ 示例:添加/删除约束
SQL
-- 添加主键
ALTER TABLE users ADD PRIMARY KEY (id);
-- 添加外键
ALTER TABLE orders ADD CONSTRAINT fk_customer
FOREIGN KEY (customer_id) REFERENCES customers(id);
-- 添加唯一约束
ALTER TABLE users ADD UNIQUE (email);
-- 删除外键
ALTER TABLE orders DROP FOREIGN KEY fk_customer;
-- 删除唯一约束
ALTER TABLE users DROP INDEX email;
❓ 常见问题
Q 外键会影响性能?
A 会有一点(写入时检查约束),但保证数据完整性。高并发可在应用层检查。
Q NULL 和 UNIQUE 的关系?
A UNIQUE 允许多个 NULL(MySQL 中 NULL != NULL)。
Q CHECK 约束能用函数吗?
A MySQL 8.0+ 支持 CHECK 中使用表达式,但不支持子查询。
Q CHECK 约束在 5.7 有效吗?
A 无效。MySQL 5.7 只语法接受 CHECK 但不执行,8.0.16+ 才真正强制执行。
Q 删除外键后数据还在吗?
A 在。删除外键只删约束关系,不删除任何数据。
📖 小节
- PRIMARY KEY 唯一标识,自动创建聚簇索引
- FOREIGN KEY 建立表关系,支持 CASCADE/SET NULL/RESTRICT
- UNIQUE 值不重复,允许 NULL
- NOT NULL 不允许 NULL
- CHECK(8.0+)确保值满足条件
📝 作业
-
基础题(难度⭐):创建带完整约束的用户表。
-
进阶题(难度⭐⭐):创建外键并测试级联删除。
-
挑战题(难度⭐⭐⭐):设计一个带 CHECK 约束的产品表,验证约束生效。