update触发器中old和new均可读,但仅before阶段new可修改,after阶段二者均只读;old在任何时机都不可写,否则报错error 1362。

UPDATE触发器里OLD和NEW到底谁可用、谁可改
只有 BEFORE UPDATE 触发器能同时读 OLD 和 NEW,且仅 NEW 可被修改;AFTER UPDATE 里两者都只读,改 NEW 直接报错或静默失效。
常见错误现象:写了 SET NEW.updated_at = NOW(); 却发现字段没更新——八成是触发器定义成了 AFTER。用 SHOW CREATE TRIGGER trigger_name; 确认时机。
-
OLD在任何触发时机下都不可写,哪怕在BEFORE阶段赋值也会报ERROR 1362 (HY000): Updating of OLD row is not allowed in trigger -
NEW字段若在表定义中为NOT NULL,在BEFORE中设为NULL会中断执行 - 主键或唯一键字段在
BEFORE UPDATE中被NEW修改,可能引发重复键冲突,需提前查重
字段名大小写和特殊字符怎么处理
OLD.column_name 和 NEW.column_name 的字段名必须与表定义**完全一致**,包括大小写。MySQL 默认区分大小写(尤其在 Linux 环境),OLD.Username 和 OLD.username 是两个不同引用。
如果列名含空格、短横线或关键字(如 order、group),必须用反引号包裹:
SET @old_status = OLD.`order status`; SET @new_id = NEW.`user-id`;
漏掉反引号会导致语法错误,而大小写不一致则可能返回 NULL 或静默失败——查日志时发现字段值为空,先核对表结构定义。
DELETE 和 INSERT 触发器中OLD/NEW的可用边界
DELETE 触发器只有 OLD,INSERT 只有 NEW,这是硬性限制。试图在 BEFORE DELETE 中读 NEW.id 会直接报错:NEW is not available in DELETE triggers。
典型误用场景:
- 在
AFTER INSERT里想记录“插入前状态”——不存在,OLD根本不可用 - 用
CONCAT(OLD.content, ' deleted')拼日志,但content是TEXT类型,在 MySQL 5.7 某些版本中可能返回NULL(非OLD失效,而是大字段上下文处理异常) - 跨库操作时写
OLD.other_db.table.col——MySQL 不支持这种跨库点号引用,必须用完整限定名other_db.table.col且确保权限允许
BEFORE UPDATE中改NEW没生效?检查这三件事
自动填充时间戳、清洗数据、校验逻辑失败却没拦住写入,往往不是语法错,而是执行链路断在了隐性环节。
- 触发器定义在
AFTER而非BEFORE:这是最常见原因,AFTER阶段数据已落盘,改NEW完全无效 - 目标字段有默认值或
NOT NULL约束:比如字段定义为updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,再在触发器里设NEW.updated_at可能被覆盖或冲突 - 触发器体里没加
DELIMITER切换,导致分号被当成语句结束,后续逻辑未被纳入触发器体——尤其多行BEGIN...END块时极易踩坑
真正难调试的是字段类型隐式转换:比如 NEW.status 是 TINYINT,你赋值字符串 'active',MySQL 可能转成 0 而不报错,结果值不对却找不到源头。











