INSERT触发器只能读NEW、不能访问OLD,否则报ERROR 1363;UPDATE需用空值安全比较如NOT (OLD.col NEW.col);DELETE只能读OLD、不可用NEW;字段名须严格匹配表定义,大小写和关键字均需注意。

INSERT触发器里只能读NEW,用OLD直接报错
MySQL在INSERT触发器中根本不存在OLD上下文,只要出现OLD.id或OLD.name这类写法,执行时必然报错:ERROR 1363: There is no OLD row in on INSERT trigger。这不是语法警告,是硬性限制。
常见误操作包括:
- 在
AFTER INSERT里给NEW.id = LAST_INSERT_ID()赋值——无效,NEW.id已确定,赋值无效果 - 想“兼容”三种事件,在一个触发器里统一写
IF OLD.id IS NOT NULL THEN ...——INSERT下这行就炸 - 复制UPDATE触发器逻辑过来,漏删
OLD相关字段访问
NEW在BEFORE INSERT中可写,比如自动填充NEW.created_at = NOW();在AFTER INSERT中只读,适合记录日志或关联更新其他表。
UPDATE触发器必须同时处理NULL比较陷阱
判断字段是否真被修改,不能只写OLD.status != NEW.status。只要任一端为NULL,整个表达式结果恒为NULL(即逻辑假),条件永远不成立。
正确做法是用空值安全比较:
- 推荐:
NOT (OLD.status NEW.status)(是MySQL空值安全等于运算符) - 手动拆解:
(OLD.status IS NULL) != (NEW.status IS NULL) OR OLD.status != NEW.status - 字符串统一转空串:
IFNULL(OLD.name, '') != IFNULL(NEW.name, '')
别在触发器里对TEXT字段用CONCAT(OLD.content, ' updated')——某些MySQL 5.7版本会静默返回NULL,不是OLD失效,而是大字段上下文异常。
DELETE触发器中NEW完全不可用,连读都不行
DELETE场景下NEW根本不存在,任何引用都会触发ERROR 1363: There is no NEW row in on DELETE statement。这是最常被忽略的硬约束,尤其当从UPDATE触发器复制代码过来时。
OLD是唯一能拿到被删行数据的方式,且在BEFORE DELETE和AFTER DELETE中都有效(只读):
-
BEFORE DELETE:适合做软约束检查,比如查子表是否有依赖记录 -
AFTER DELETE:适合归档,例如INSERT INTO goods_history SELECT OLD.*
注意:SELECT OLD.*不能直接插入到结构完全相同的表,主键/唯一键会冲突。历史表应设自增id,加op_type ENUM('DELETE')和op_time等辅助字段。
字段名大小写和拼写必须和表定义一字不差
MySQL不校验OLD/NEW字段是否存在,直到触发时才出问题。写错就静默返回NULL,极难排查。
典型坑点:
- 建表字段是
user_name,却写成OLD.username或OLD.USER_NAME→ 返回NULL - 字段含关键字如
order、status,没加反引号 →ERROR 1064 - 建表时用双引号定义了大小写敏感字段(如
"Name"),触发器里也得写OLD."Name"
不确定字段名?先执行DESCRIBE your_table确认真实拼写,别凭记忆或IDE提示瞎猜。
TEXT或BLOB,加上函数调用(比如CONCAT)导致上下文处理异常,而不是触发器机制失效。


















