INSERT触发器仅响应INSERT/REPLACE/LOAD DATA,不响应UPDATE或DELETE;UPDATE触发器仅在字段值实际变化时触发;DELETE触发器不响应TRUNCATE且OLD仅触发时有效。

INSERT触发器只能响应INSERT/REPLACE/LOAD DATA,不响应UPDATE或DELETE
触发器不是按“你想让它响应什么”来决定行为的,而是由实际执行的DML语句类型严格绑定。只要执行的是INSERT INTO、REPLACE INTO或LOAD DATA INFILE,就会激活INSERT触发器;哪怕SQL里写了UPDATE但实际没改任何行,也不会触发它。
常见错误现象:INSERT触发器在UPDATE语句后意外执行——基本是误把表名写错,或者触发器创建时ON子句指定的表和实际操作表不一致。
-
NEW可用,OLD不可用(因为无旧数据) - 只在行级生效:插入100行,触发100次(MySQL不支持语句级)
-
BEFORE INSERT中可修改NEW.column值,比如补全created_at或做格式校验 -
AFTER INSERT中不能再改NEW字段,否则报错Can't update table 't' in stored function/trigger
UPDATE触发器只对真正变化的字段值触发
UPDATE触发器不是“只要写了UPDATE就触发”,而是逐行判断:该行中至少有一个字段的值在UPDATE前后不等,才会触发。例如UPDATE user SET status=1 WHERE id=100,但如果当前status已经是1,这一行就不会进入触发器逻辑。
这个特性常被忽略,导致审计日志漏记录、缓存未更新等问题。
-
OLD和NEW都可用,分别指向变更前后的整行数据 -
BEFORE UPDATE适合做字段级拦截(如禁止降级权限)、生成updated_at、或预计算关联字段 -
AFTER UPDATE适合写变更日志、通知下游服务、清理冗余索引表 - 不能在
AFTER UPDATE中修改NEW,否则报错
DELETE触发器不响应TRUNCATE,且OLD数据仅在触发时存在
TRUNCATE TABLE是DDL操作,完全绕过所有DELETE触发器——这是高频生产事故点。很多团队以为加了删除日志触发器就万事大吉,结果定时任务用TRUNCATE清表,日志表一片空白。
另外,DELETE触发器中的OLD只在触发器执行期间有效;一旦触发器退出,原行已从表中物理移除,无法回查。
-
OLD可用,NEW不可用 -
BEFORE DELETE可基于OLD做校验(如防止删除主订单),但不能阻止级联删除本身 -
AFTER DELETE最常用:写日志、删关联附件、发MQ消息——此时OLD仍可读,但表中该行已不存在 - 若需TRUNCATE级操作留痕,必须改用
DELETE FROM ... WHERE 1=1并确保有WHERE(避免全表锁升级)
三个事件的BEFORE/AFTER选择直接影响数据一致性
选错时机可能让业务逻辑失效。比如想用触发器自动填充updated_at,却建在AFTER UPDATE里——这时再改NEW.updated_at会直接报错;而放在BEFORE UPDATE里就能安全赋值。
又比如要实现“余额不足禁止扣款”,必须用BEFORE UPDATE读OLD.balance、算本次扣减、不够就SIGNAL SQLSTATE '45000'中断;放到AFTER里就晚了,钱已经扣掉。
-
BEFORE INSERT/UPDATE:唯一能合法修改NEW的时机 -
AFTER INSERT/UPDATE/DELETE:唯一能安全访问完整OLD或NEW且不影响当前事务原子性的时机 - 跨表操作(如更新订单后减库存)建议放
AFTER,避免触发器内死锁
OLD和NEW的生命周期、TRUNCATE的豁免权、以及字段值是否真变化这三个点,线上问题八成出在这儿。

















