AFTER触发器中NEW不可修改,因数据已写入表、NEW仅为只读快照;BEFORE触发器中NEW可写,能影响最终入库值;误用时机需通过SHOW CREATE TRIGGER或查询INFORMATION_SCHEMA确认。

因为 AFTER 触发器不允许修改 NEW 伪记录,这是数据库引擎的硬性限制,不是语法或权限问题。
为什么 NEW 在 AFTER 触发器里改不了?
MySQL(以及大多数主流 SQL 数据库)在 AFTER 阶段已将数据写入表中,事务即将提交。此时 NEW 只是只读快照,任何对 SET NEW.col = ... 的尝试都会触发错误:ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger。
而 BEFORE 触发器运行在写入前,NEW 是可写的“待插入/待更新行”,修改它会直接影响最终落库的数据。
-
BEFORE INSERT:可设NEW.created_at = NOW()、NEW.email = TRIM(LOWER(NEW.email)) -
BEFORE UPDATE:可拦截非法状态变更,比如IF OLD.status = 'shipped' AND NEW.status = 'pending' THEN SIGNAL SQLSTATE '45000' ... -
AFTER INSERT/UPDATE/DELETE:只能读NEW.id、OLD.name,用于日志、统计、通知等不干预主操作的场景
如何快速确认触发器是否误用了 AFTER 时机?
直接查系统表或用 SHOW CREATE TRIGGER 看定义:
SHOW CREATE TRIGGER order_status_audit;
重点关注返回结果中的 BEFORE INSERT / AFTER UPDATE 这类字样。如果业务逻辑需要改数据(比如补默认值、转大小写、校验后修正),但触发器写的是 AFTER,那就必须重写为 BEFORE。
- 别依赖注释或命名判断——
CREATE TRIGGER log_after_update也可能实际是BEFORE,以 DDL 定义为准 - 执行
SELECT TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA = 'your_db';可批量检查所有触发器的时机
NEW 和 OLD 的可用性与 NULL 处理陷阱
即使时机选对了,NEW/OLD 的可用性也严格绑定事件类型:
-
INSERT:只有NEW合法,OLD不存在(引用会报错) -
DELETE:只有OLD合法,NEW不存在 -
UPDATE:两者都存在,但比较字段是否被修改时,不能直接写IF NEW.status != OLD.status—— 若任一为NULL,整条表达式为UNKNOWN,需显式处理:IF (NEW.status IS NULL) != (OLD.status IS NULL) OR NEW.status != OLD.status
真正容易被忽略的点是:哪怕你写了 BEFORE UPDATE,如果 SQL 语句本身没真正修改某列(比如 UPDATE t SET name = name),NEW.name 和 OLD.name 值相同,但仍是合法可读可写的。关键不在“有没有变”,而在“能不能改”——这个权力只属于 BEFORE。

















