before delete触发器才是级联删除的正确入口,必须用它而非after delete,因after执行时父记录已删,子表残留数据会成孤儿;before可保证事务原子性,且仅能基于old引用关联字段,避免误删或全表删除。

BEFORE DELETE 触发器才是级联删除的正确入口
想靠触发器模拟 ON DELETE CASCADE,必须用 BEFORE DELETE,而不是 AFTER DELETE。因为 AFTER 触发时父记录已删,子表数据若还存在,就变成“孤儿记录”,违背级联语义。
常见错误是写成 AFTER DELETE 并直接删子表——它能执行,但不参与事务回滚:外层事务失败回滚后,父记录恢复了,子记录却永久丢失。
-
BEFORE DELETE中执行DELETE FROM child_table WHERE parent_id = OLD.id,能保证原子性(InnoDB 下整个操作要么全成功,要么全失败) - MyISAM 表不支持事务,
BEFORE DELETE是唯一可控时机,但失败后无回滚能力,需额外校验 - 触发器里不能操作触发表本身,所以
DELETE FROM parent_table这类语句会报错ERROR 1442
OLD.id 是唯一可用的关联值,NEW.id 在 DELETE 中不存在
DELETE 操作没有“新值”,NEW.id 为 NULL 或未定义,硬写会出错或误删。所有关联逻辑必须基于 OLD 引用。
- 子表删除条件必须明确写成
WHERE order_id = OLD.id,漏掉OLD.前缀就会删全表 - 若主键是复合键(如
(dept_id, role)),要写成WHERE dept_id = OLD.dept_id AND role = OLD.role - 对 UUID 主键,确保比较使用相同类型:
WHERE category_id = OLD.id,不要隐式转换导致索引失效
触发器不递归,二级关联需手动补全
一个 BEFORE DELETE 触发器只管直接子表。如果子表(如 order_items)还有下级表(如 item_logs),这个触发器完全不会触碰后者。
- 必须在
order_items上再建一层BEFORE DELETE触发器,清理item_logs - 避免循环触发:比如
orders → order_items → orders,检查逻辑中不能反向引用父表 - 深度超过两层时,维护成本陡增,此时应考虑重构为应用层事务或改用外键
比触发器更稳的方案:优先用原生外键
除非你卡在 MyISAM、无法 ALTER TABLE、或必须在删除前插入审计日志,否则别自己写触发器级联。
-
ALTER TABLE order_items ADD CONSTRAINT fk_order_id FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE是零配置、高性能、事务安全的首选 - 外键约束由引擎原生处理,在内存完成,无 SQL 解析开销;触发器每次 DELETE 都要启动解释器,QPS 高时明显拖慢
- 外键自动维护参照完整性,触发器容易因 WHERE 条件写错、字段名拼错、NULL 处理不当而漏删或误删
真正需要触发器的场景极少——多数时候,是把简单问题复杂化了。写完触发器,记得问一句:这张表真不能换成 InnoDB 吗?











