insert触发器仅可用new,delete触发器仅可用old,update触发器二者皆可用;before中new可写,after中new只读,old始终只读;字段名错误或null比较不当将导致静默失败或逻辑错误。

不能靠“理解语义”去猜 NEW 和 OLD 谁能用、谁不能读——MySQL 会在触发器创建或执行时直接报错,且错误信息明确指向语法禁区。关键不是记概念,而是按操作类型+触发时机查表执行。
INSERT/UPDATE/DELETE 触发器里哪些伪记录可用?
这是硬性限制,写错就报错,不给运行机会:
- INSERT 触发器:只允许访问
NEW;写OLD.id立刻报ERROR 1363 (HY000): There is no OLD row in on INSERT trigger - DELETE 触发器:只允许访问
OLD;写NEW.name立刻报NEW is not available in DELETE triggers - UPDATE 触发器:
OLD和NEW都可用,且仅在此类触发器中二者共存
常见误操作是建了个 AFTER INSERT 触发器,却在逻辑里写了 IF OLD.status IS NOT NULL THEN...——这不是逻辑错,是 MySQL 直接拒绝加载该触发器。
BEFORE vs AFTER 中 NEW 能不能改?
NEW 字段只在 BEFORE 触发器中可写,AFTER 里赋值无效,还会报错:
-
BEFORE INSERT:可设NEW.created_at = NOW()、NEW.id = UUID(),影响最终插入值 -
BEFORE UPDATE:可设NEW.updated_at = NOW(),修改即将写入的字段 -
AFTER INSERT或AFTER UPDATE:对NEW赋值无效果;若尝试更新其他表(如UPDATE log_table SET ...),可能因事务上下文受限而失败
注意:OLD 在所有时机下都只读,任何写操作都会被拒绝。
字段名写错不会报语法错,但会静默返回 NULL
MySQL 不校验 OLD 或 NEW 后的字段是否存在,直到触发时才出问题,排查困难:
- 表字段是
user_name,却写成OLD.username或OLD.USER_NAME→ 返回NULL,不是报错 - 字段含空格或关键字(如
`order status`),没加反引号 → 报ERROR 1064 - 建表时用了双引号定义大小写敏感字段(如
"Name"),触发器里也得写OLD."Name"
不确定字段名?先执行 DESCRIBE your_table 确认真实拼写,别凭记忆写。
比较新旧值时,NULL 处理最容易翻车
OLD.status != NEW.status 在任一端为 NULL 时结果恒为 NULL(即 false),条件永远不成立:
- 推荐写法:
OLD.status NEW.status(MySQL 空安全等于)结果为0表示不等,1表示相等(含两者都为NULL) - 手动拆解也可行:
(OLD.status IS NULL) != (NEW.status IS NULL) OR OLD.status != NEW.status - 别用
CONCAT(OLD.content, ' updated')处理 TEXT 字段——某些 MySQL 5.7 版本会返回NULL,不是OLD没拿到,而是大字段上下文异常
真正容易被忽略的是:你看到日志里某字段值为空,第一反应常是“OLD 没拿到”,但大概率是字段类型为 TEXT 或 BLOB,加上函数调用导致上下文处理异常,而不是触发器机制失效。











