只有BEFORE INSERT和BEFORE UPDATE触发器可修改NEW字段值,AFTER阶段NEW为只读快照,赋值无效且静默忽略;字段名须严格匹配表定义(大小写、下划线、反引号均敏感);禁止在触发器内UPDATE本表;自增ID在BEFORE中为NULL,AFTER中仅可读。

BEFORE触发器中才能修改NEW字段值
只有BEFORE INSERT和BEFORE UPDATE触发器允许对NEW字段赋值,AFTER阶段的NEW是只读快照,改了也无效,且不会报错——容易误以为“写成功了”,实际被静默忽略。
常见错误现象:ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger,说明你在AFTER里写了SET NEW.x = ...;或者没报错但逻辑没生效,大概率是时机选错了。
-
BEFORE INSERT:可用于填充默认值,如SET NEW.created_at = NOW()、SET NEW.status = 'pending' -
BEFORE UPDATE:适合做数据清洗或联动更新,如SET NEW.phone = REPLACE(NEW.phone, '-', '')、SET NEW.updated_at = NOW() -
AFTER INSERT/UPDATE:只能读NEW,不能写;适合记录日志、写入关联表(如INSERT INTO log_table VALUES (NEW.id, NEW.name))
字段名必须严格匹配表定义,大小写敏感
NEW不是变量,也不是函数返回值,它是 MySQL 在触发上下文中自动注入的伪记录,字段名必须与表结构完全一致——包括大小写、下划线、甚至反引号包裹的特殊字符。
常见错误现象:SELECT NEW.username返回NULL,而实际字段是user_name;或建表时用了双引号定义"Name",触发器里却写NEW.Name,结果报错或取空。
- 执行
DESCRIBE your_table确认真实字段名,逐字比对 - 字段含空格、横线或关键字(如
order、status),必须用反引号:NEW.`order_status` - 大小写敏感文件系统(如Linux)上,
NEW.ID≠NEW.id,哪怕表定义里是小写,也不能大写访问 - MySQL 创建触发器时不校验字段是否存在,直到真正触发才报错,所以别依赖语法检查
不能在触发器里用UPDATE语句改本表当前行
想“根据新值更新同一张表的其他字段”?别写UPDATE your_table SET x = ... WHERE id = NEW.id。MySQL 明确禁止在触发器内修改正在被操作的表,会直接报错:Can't update table 'xxx' in stored function/trigger。
这不是权限问题,是 MySQL 的递归保护机制。哪怕加了WHERE精准匹配,也会被拦截。
- 正确做法:全用
SET NEW.xxx = ...在BEFORE阶段完成修改 - 例如 status 变为
'done'时自动填finished_at,就写SET NEW.finished_at = NOW(),不要绕路 - 如果逻辑复杂到无法用
SET表达(比如要查另一张大表做判断),应移出触发器,改由应用层控制 - 临时表中转、存储过程调用等“绕开”方案存在并发和一致性风险,不推荐
NEW.id在INSERT触发器里的行为边界
自增主键的NEW.id在BEFORE INSERT中是NULL(除非显式插入了值),只有到AFTER INSERT才可稳定读取。但此时不能再写回NEW——它已只读。
常见误解:想在BEFORE INSERT里读NEW.id做业务判断(比如生成关联码),结果拿到NULL;或在AFTER里试图SET NEW.id = ...,语法通过但无效果。
- 确认主键是否真为
AUTO_INCREMENT,且插入语句没显式指定该字段(包括传0、NULL或DEFAULT) -
BEFORE INSERT中需要ID参与逻辑?不可行——要么改用应用层获取LAST_INSERT_ID(),要么用BEFORE生成非自增唯一标识(如UUID) -
AFTER INSERT中NEW.id可用,但仅限读取;用于插入日志、通知、统计等副作用操作是安全的 - 事务回滚后
NEW.id仍有效,但ID已被消耗,下次插入会跳号——这点常被忽略,影响幂等设计


















