简介
外键是 MySQL 中用来建立和强制两个表之间关联关系的约束,作用是:
- 保证数据的参照完整性 :子表(从表)的外键字段值,必须在主表(父表)的主键 / 唯一键中存在,或者为
NULL。 - 实现表与表的关联 :比如
emp员工表的dept_id关联dept部门表的id,确保员工不会分配到一个不存在的部门。 - 配合级联操作,可以实现主表数据更新 / 删除时,子表数据自动同步。
删除主表中的数据,必须保证存有外键的子表中的相关数据都要被删除之后才能删除主表中的数据,从而保证了数据的完整性。


核心概念
表格
| 术语 | 含义 | 对应我们的表 |
|---|---|---|
| 主表(父表) | 被引用的表,包含主键 / 唯一键 | dept 部门表(id 是主键) |
| 子表(从表) | 引用外键的表,包含外键字段 | emp 员工表(dept_id 是外键) |
| 外键字段 | 子表中用来关联主表的字段 | emp.dept_id |
| 外键名称 | 给这个约束起的名字,方便后续删除 / 修改 | 比如 fk_emp_dept |
二、外键的完整语法
1. 两种添加外键的方式
方式 1:创建表时直接添加外键(CREATE TABLE)
sql
CREATE TABLE 表名(
字段名 数据类型,
...
-- 外键约束语法
[CONSTRAINT 外键名称] FOREIGN KEY (外键字段名)
REFERENCES 主表(主表列名)
[ON DELETE 级联操作] [ON UPDATE 级联操作]
);
CONSTRAINT 外键名称:可选,建议手动命名,方便后续管理;不写则 MySQL 自动生成。FOREIGN KEY (外键字段名):子表中作为外键的字段。REFERENCES 主表(主表列名):关联的主表和主表的主键 / 唯一键。ON DELETE/ON UPDATE:可选,级联操作规则(后面详细讲)。
方式 2:表已创建,后续添加外键(ALTER TABLE)
sql
alter table emp3
add constraint fk_emp_dept
foreign key (dept_id)
references dept(id);
ALTER TABLE 子表名
ADD CONSTRAINT 外键名称
FOREIGN KEY (外键字段名) --子表中的字段
REFERENCES 主表(主表列名) --父表中的字段
[ON DELETE 级联操作] [ON UPDATE 级联操作];
- 适合表已经建好、数据已经插入后,再补加外键约束。
2. 删除外键的语法
sql
alter table emp3 drop foreign key fk_emp3_dept;
ALTER TABLE 子表名 DROP FOREIGN KEY 外键名称;
- 必须通过外键名称删除,不能通过字段名删除,所以建议创建时手动命名。
注意事项
- 存储引擎要求 :
- 只有
InnoDB引擎支持外键,MyISAM不支持!创建表时默认是 InnoDB,无需手动指定。
- 只有
- 字段类型必须一致 :
- 子表外键字段的数据类型、长度、字符集,必须和主表被引用的字段完全一致。
- 比如主表
dept.id是INT,子表emp.dept_id也必须是INT,不能是BIGINT或VARCHAR。
- 主表必须有索引 :
- 主表被引用的列必须是主键(PRIMARY KEY) 或 唯一键(UNIQUE KEY),否则无法创建外键。
- 外键字段可以为 NULL :
- 子表外键字段可以插入
NULL,表示该记录不关联主表(比如员工未分配部门)。
- 子表外键字段可以插入
- 插入数据顺序 :
- 必须先插入主表数据,再插入子表数据,否则子表外键值在主表不存在,会报错。
- 比如:必须先在
dept插入部门,再在emp插入员工,否则dept_id无效。
- 删除数据顺序 :
- 必须先删除子表数据,再删除主表数据(默认 RESTRICT 规则下),否则会报错。
- 比如:要删除
dept中 id=1 的研发部,必须先删除emp中所有dept_id=1的员工,否则无法删除。
- 外键会影响性能 :
- 每次插入 / 更新子表数据,都会校验主表,会有性能开销;高并发、大数据量场景,很多公司会选择业务层实现关联,不使用数据库外键。
- MySQL 外键约束行为(ON DELETE / ON UPDATE)深度详解
外键的核心价值,除了强制参照完整性 ,更关键的是通过约束行为(级联规则),定义「主表数据发生变化时,子表该如何响应」,完美解决业务中的关联数据同步问题。
5 种核心约束行为(全场景覆盖)
MySQL 支持 5 种外键约束行为,用「部门 - 员工」的业务场景,讲透每一种的效果、适用场景和优缺点:
表格
| 约束行为 | 英文全称 | 核心逻辑 | 主表删除 / 更新时,子表的动作 | 适用场景 | 风险提示 |
|---|---|---|---|---|---|
| RESTRICT | Restrict | 限制 / 阻止 | 直接拒绝主表的操作 ,报错 Cannot delete or update a parent row |
防止误删 / 误改有子表关联的主表数据(默认行为) | 无风险,最安全 |
| NO ACTION | No Action | 无动作 | 和 RESTRICT 完全等价,MySQL 中两者效果 100% 一致 |
标准 SQL 写法,和 RESTRICT 二选一即可 |
同 RESTRICT |
| CASCADE | Cascade | 级联 | 主表删除 / 更新,子表同步执行相同操作 | 部门删除,对应员工自动删除;部门 ID 修改,员工部门 ID 同步更新 | 高危!误删主表会直接清空子表关联数据 |
| SET NULL | Set Null | 置空 | 主表删除 / 更新,子表外键字段自动设为 NULL | 部门删除,员工部门 ID 置空(员工保留,标记为「未分配部门」) | 子表外键字段必须允许为 NULL,否则报错 |
| SET DEFAULT | Set Default | 设默认值 | 主表删除 / 更新,子表外键字段自动设为默认值 | 部门删除,员工部门 ID 自动设为默认部门(比如总经办) | MySQL 8.0 仅 InnoDB 引擎支持,且需子表外键字段有明确 DEFAULT 值,极少使用 |
1. 默认行为:RESTRICT / NO ACTION(最安全,零风险)
语法
sql
-- 创建表时指定(默认可以省略,因为是默认行为)
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id)
ON DELETE RESTRICT ON UPDATE RESTRICT;
效果演示
-
场景:
dept表有id=1(研发部),emp表有dept_id=1的员工(张三、李四) -
执行删除主表操作:
DELETE FROM dept WHERE id = 1; -
结果 :直接报错,拒绝删除,提示:
plaintext
Cannot delete or update a parent row: a foreign key constraint fails (`test`.`emp`, CONSTRAINT `fk_emp_dept` FOREIGN KEY (`dept_id`) REFERENCES `dept`(`id`)) -
核心逻辑:只要子表有对应数据,就绝对不允许主表删除 / 更新,从根源上避免「孤儿数据」(员工有部门 ID,但部门不存在)。
-
适用场景:绝大多数业务系统,尤其是核心数据(比如订单 - 商品、用户 - 账户),防止误操作。
2. 级联操作:CASCADE(最常用,同步数据)
语法
sql
-- 级联删除 + 级联更新
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id)
ON DELETE CASCADE ON UPDATE CASCADE;
效果演示
- 场景 1:级联删除(ON DELETE CASCADE)
- 主表
dept删除id=1(研发部) - 子表
emp中所有dept_id=1的员工(张三、李四)自动被删除
- 主表
- 场景 2:级联更新(ON UPDATE CASCADE)
- 主表
dept更新id=1→id=10 - 子表
emp中所有dept_id=1的员工,dept_id自动同步为 10
- 主表
- 核心逻辑:主表的操作「穿透」到子表,完全同步,无需手动维护。
- 适用场景:强关联、从属关系的数据(比如订单 - 订单明细、文章 - 评论),主表删除,子表数据无保留价值。
- ⚠️ 高危警告 :
- 误删主表会直接清空子表所有关联数据,生产环境必须谨慎使用!
- 建议仅在「主表删除 = 子表数据彻底无用」的场景使用,比如「购物车 - 商品」不适合(删除商品不能删用户购物车)
3. 置空操作:SET NULL(保留数据,解除关联)
语法
sql
-- 主表删除/更新时,子表dept_id设为NULL
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id)
ON DELETE SET NULL ON UPDATE SET NULL;
效果演示
- 场景:
dept表删除id=1(研发部) - 子表
emp中所有dept_id=1的员工,dept_id自动变为 NULL,员工数据保留 - 核心逻辑:保留子表数据,仅解除与主表的关联,标记为「未关联」。
- 适用场景 :
- 部门解散,但员工需要保留(比如调岗、待分配)
- 商品下架,但用户订单 / 购物车需要保留(外键设为 NULL,标记为「已下架商品」)
- ⚠️ 前提条件 :
- 子表外键字段(
emp.dept_id)必须允许为 NULL (不能加NOT NULL约束),否则创建外键直接报错。
- 子表外键字段(
4. 设默认值:SET DEFAULT(极少使用,兼容性差)
语法
sql
-- 子表dept_id必须设置DEFAULT默认值,比如DEFAULT 5(总经办ID=5)
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id)
ON DELETE SET DEFAULT ON UPDATE SET DEFAULT;
效果演示
- 场景:
dept表删除id=1(研发部) - 子表
emp中所有dept_id=1的员工,dept_id自动设为默认值 5(总经办) - 核心逻辑:主表数据失效时,子表自动关联到默认主表数据。
- 适用场景 :
- 部门删除,员工自动归属到「总经办」
- 商品删除,订单自动关联到「已下架商品」虚拟商品
- ⚠️ 限制说明 :
- MySQL 8.0 仅 InnoDB 引擎支持,旧版本 / 其他引擎不支持
- 子表外键字段必须有明确的
DEFAULT值,且该值必须在主表中存在(否则报错) - 实际生产中极少使用,兼容性和灵活性差,一般用
SET NULL+ 业务逻辑替代
四、组合使用示例(真实业务最佳实践)
场景 1:部门 - 员工(最常用组合)
- 需求:部门删除时,员工保留(部门 ID 置空);部门 ID 修改时,员工部门 ID 同步更新
sql
CREATE TABLE emp(
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
dept_id INT COMMENT '部门ID(允许为NULL,未分配部门)',
-- 组合规则:删除置空,更新级联
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id)
ON DELETE SET NULL -- 部门删除,员工dept_id置空
ON UPDATE CASCADE -- 部门ID修改,员工dept_id同步更新
) COMMENT '员工表';
场景 2:订单 - 订单明细(强关联)
- 需求:订单删除,明细自动删除;订单 ID 修改,明细同步更新
sql
CREATE TABLE order_detail(
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL COMMENT '订单ID',
product_id INT NOT NULL COMMENT '商品ID',
-- 级联删除+级联更新
CONSTRAINT fk_detail_order FOREIGN KEY (order_id) REFERENCES `order`(id)
ON DELETE CASCADE ON UPDATE CASCADE
) COMMENT '订单明细表';
场景 3:商品 - 库存(严格限制,防止误删)
- 需求:只要有库存,就绝对不允许删除商品
sql
CREATE TABLE stock(
id INT AUTO_INCREMENT PRIMARY KEY,
product_id INT NOT NULL COMMENT '商品ID',
stock_num INT NOT NULL COMMENT '库存数量',
-- 默认RESTRICT,拒绝删除/更新
CONSTRAINT fk_stock_product FOREIGN KEY (product_id) REFERENCES product(id)
ON DELETE RESTRICT ON UPDATE RESTRICT
) COMMENT '库存表';
1. 字段类型与约束要求
- 子表外键字段的数据类型、长度、字符集,必须和主表被引用的主键 / 唯一键完全一致
SET NULL要求子表外键字段允许为NULL,SET DEFAULT要求子表外键字段有明确DEFAULT值- 主表被引用的列必须是主键(PRIMARY KEY) 或 唯一键(UNIQUE KEY),否则无法创建外键
2. 存储引擎要求
- 只有
InnoDB引擎支持外键,MyISAM不支持(创建外键不会报错,但不生效) SET DEFAULT仅 InnoDB 引擎支持,其他引擎不支持
3. 性能影响
- 外键约束会在每次插入 / 更新 / 删除时,强制校验主表数据,会有一定性能开销
- 高并发、大数据量的互联网系统(比如电商、社交),通常会关闭数据库外键,在业务层实现关联逻辑,以提升性能
- 内部管理系统、数据一致性要求高的场景,推荐使用外键,减少业务代码复杂度
4. 数据插入 / 删除顺序
- 插入顺序:必须先插入主表数据,再插入子表数据(否则子表外键值在主表不存在,报错)
- 删除顺序 :
RESTRICT/NO ACTION:必须先删子表,再删主表CASCADE:直接删主表,子表自动删除SET NULL/SET DEFAULT:直接删主表,子表自动置空 / 设默认值
5. 外键删除
- 删除外键必须通过外键名称 ,不能通过字段名删除,所以创建时建议手动命名(比如
fk_emp_dept) - 语法:ALTER TABLE
empDROP FOREIGN KEYfk_emp_dept;
七、外键约束行为速查表(直接保存)
表格
| 操作 | RESTRICT/NO ACTION | CASCADE | SET NULL | SET DEFAULT |
|---|---|---|---|---|
| 主表删除 | 拒绝删除,报错 | 子表同步删除 | 子表外键设为 NULL | 子表外键设为默认值 |
| 主表更新 | 拒绝更新,报错 | 子表同步更新 | 子表外键设为 NULL | 子表外键设为默认值 |
| 安全性 | 最高 | 最低(高危) | 中 | 中 |
| 适用场景 | 核心数据、防误删 | 强关联从属数据 | 保留数据、解除关联 | 自动归属默认数据 |