bigywater.tech
MySQL / InnoDB · 23

外键级联删除:父行删除后子行去了哪里

建三张外键配置不同的子表,对同一个父行删除操作,验证CASCADE、SET NULL、默认RESTRICT三种约束给出的三种不同结局。

CASCADE删除
子行随父行一起消失
SET NULL置空
子行保留但断开关联
RESTRICT拒绝
默认策略下删除操作被挡住

做的事:同一个父行,三种外键约束的反应

一张父表,三张子表分别配置不同的ON DELETE行为。

建表CREATE TABLE parent1 (id INT PRIMARY KEY); CREATE TABLE child1 (id INT PRIMARY KEY, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent1(id) ON DELETE CASCADE); CREATE TABLE child2 (id INT PRIMARY KEY, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent1(id) ON DELETE SET NULL); CREATE TABLE child3 (id INT PRIMARY KEY, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent1(id)); -- 默认RESTRICT 插入parent1(1),(2);child1/child2各插入3行(2行指向id=1,1行指向id=2)

验证:CASCADE把子行删掉了

执行DELETE FROM parent1 WHERE id=1。

结果DELETE FROM parent1 WHERE id=1; SELECT * FROM child1; → 只剩:(3, 2) ← id=1,2的两行(parent_id=1)被自动删除了

执行的只是对parent1一张表的DELETE,child1里两行parent_id=1的记录被InnoDB自动一起删除了——CASCADE的意思就是"父行没了,子行也没有存在的意义,一起删"。

验证:SET NULL把外键列置空,行本身留着

同样的父行删除,child2是另一种结局。

结果SELECT * FROM child2; → (1, NULL) ← 行还在,parent_id变成NULL → (2, NULL) ← 同样行还在,parent_id变成NULL → (3, 2) ← 没受影响,因为它指向的是parent_id=2

child2里原本parent_id=1的两行,没有被删除,而是它们的 parent_id 列被自动改成了NULL——数据本身保留下来,只是"断开了跟已删除父行的关联"。

验证:默认RESTRICT,直接拒绝这次删除

没有指定ON DELETE行为,默认是最保守的策略。

结果INSERT INTO child3 VALUES (1,2); DELETE FROM parent1 WHERE id=2; 报错: Cannot delete or update a parent row: a foreign key constraint fails (`demo`.`child3`, CONSTRAINT `child3_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent1` (`id`))
三种不同的策略对应三种不同的业务语义:CASCADE 适合"子数据完全依附于父数据、没有父数据就没有存在意义"的场景(比如订单和订单明细);SET NULL 适合"子数据本身依然有价值,只是失去了这个关联"的场景(比如员工离职后,他经手的历史记录不该被删,但可以不再关联到已注销的员工id);默认的 RESTRICT 则是最保守的选择——只要还有东西引用着这个父行,就不允许删,逼着调用方先处理掉所有子数据,避免"手滑删了一个不该删的父行"引发连锁的隐性数据丢失。

分步演示:CASCADE、SET NULL、RESTRICT的行为对比

每一步都是本文上面用MySQL跑出来的原始结果。

同一个父行删除,三种外键约束给出三种结局
这一步新验证的约束类型

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),三张外键配置不同的子表
    • ON DELETE CASCADE / SET NULL / RESTRICT:对比父行删除时的三种行为
    京ICP备2026067502号-1京公网安备11010502063487号