insert触发器只能用new,old不存在;update触发器old和new均可用,old为修改前值、new为新值;delete触发器只能用old,new非法;new和old是只读伪记录,不可赋值、不可声明、字段名须严格匹配原表。

INSERT触发器里只能用NEW,别碰OLD
在INSERT触发器中,OLD根本不存在——哪怕只是读取也会直接报错:ERROR 1363: There is no OLD row in on INSERT statement。只有NEW可用,它代表即将插入或刚插入的那行数据。
实操建议:
-
BEFORE INSERT中可修改NEW.column:比如自动填充created_at、生成uuid、校验并重写数值(如把空字符串转为NULL) -
AFTER INSERT中NEW.column只读,适合记录日志、关联更新其他表(如统计表加1) - 常见错误:在
AFTER INSERT里写SET NEW.id = LAST_INSERT_ID()——无效,NEW.id此时已确定,赋值无任何效果
UPDATE触发器里OLD和NEW都要用,但不能混用时机
OLD是修改前的快照,NEW是SQL语句中指定的新值(尚未生效)。两者字段名完全一致,但内容可能不同——这是做审计、拦截、补全的核心依据。
实操建议:
-
BEFORE UPDATE:可读可写NEW.column;OLD.column只读,适合判断是否真有变更:IF OLD.status != NEW.status THEN ... -
AFTER UPDATE:两者都只读,适合写日志:INSERT INTO log_table SET old_status = OLD.status, new_status = NEW.status - 陷阱:如果
UPDATE语句没改某字段(如SET name = name),OLD.name == NEW.name仍为true,但触发器照样执行——别指望“字段变了才触发”,得自己比对 - NULL比较必须用
IS NULL,不能用=或!=;更稳妥写法是IFNULL(OLD.col, '') != IFNULL(NEW.col, '')
DELETE触发器里只认OLD,NEW一用就崩
DELETE操作没有“新值”,所以NEW在任何DELETE触发器中都是非法的,引用即报错:ERROR 1363: There is no NEW row in on DELETE statement。OLD是唯一能拿到被删行数据的方式,且在BEFORE和AFTER中都有效(只读)。
实操建议:
-
BEFORE DELETE:适合做软约束检查,比如查子表是否有关联记录,有则SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Cannot delete parent record' -
AFTER DELETE:适合归档,例如INSERT INTO goods_history SELECT OLD.* - 注意:如果原表字段是
TEXT或BLOB类型,在某些MySQL版本(尤其是5.7)中,OLD.col在触发器内可能返回NULL,不是OLD失效,而是大字段上下文处理不一致
所有场景下NEW和OLD都不是变量,别当变量用
它们是只读的“伪记录”(transition variables),结构和原表一致,字段名可直接通过点号访问,但不能声明、不能赋值、不能加@前缀——写成@OLD.id或OLD_id会直接报错:Unknown column 'OLD.xxx' in 'field list'。
实操建议:
- 字段名大小写必须严格匹配原表定义(取决于系统
lower_case_table_names配置) - 列名含空格或关键字时,必须用反引号:
NEW.`order status - 不能对
OLD赋值,尝试SET OLD.id = 1会报ERROR 1362: Updating of OLD row is not allowed - 跨库操作需显式指定库名:
NEW.db_name.table_name.column,否则默认解析为当前库
NEW和OLD只在FOR EACH ROW上下文中存在,漏写这句或误写成FOR EACH STATEMENT会导致创建失败,报错信息往往指向NEW或OLD不可用——其实根源是语法不合法,不是变量没定义。











