OLD仅在UPDATE和DELETE触发器中可用,BEFORE/AFTER阶段均可读取但只读;INSERT中访问会报错;字段名须严格匹配表定义,含空格或关键字需用反引号包裹。

只能用 OLD,且仅在 UPDATE 和 DELETE 触发器中有效。
哪些触发器能读到修改前的值
OLD 不是变量,也不是函数返回值,它是 MySQL 在特定操作上下文中自动提供的只读伪记录。它只存在于:
-
BEFORE UPDATE和AFTER UPDATE:可读OLD.column_name,用于比对字段变化 -
BEFORE DELETE和AFTER DELETE:可读OLD.column_name,用于记录删除前状态 -
INSERT触发器里访问OLD会直接报错:ERROR 1363 (HY000): There is no OLD row in on INSERT trigger
字段名必须严格匹配原表定义
大小写、下划线、空格、关键字都得一模一样。比如原表字段是 `order status`(带空格),就必须写 OLD.`order status`;写成 OLD.order_status 或 OLD.orderstatus 都会报错 Unknown column 'OLD.xxx' in 'field list'。
常见踩坑点:
- 表字段为
user_id,却写成OLD.userId(驼峰)→ 返回 NULL 或报错 - 字段名含关键字如
order,没加反引号 →ERROR 1064 - MySQL 配置了
lower_case_table_names=1,但字段本身是大写定义,OLD.Name可能静默失败
BEFORE UPDATE 中怎么安全比对新旧值
这是最常用也最容易出错的场景。OLD 和 NEW 同时可用,但要注意:
-
OLD.column始终只读,赋值会报错:ERROR 1362 (HY000): Updating of OLD row is not allowed in trigger - 未在
UPDATE SET子句中出现的字段,NEW.column仍为原值(不是NULL),所以OLD.col = NEW.col判断是否真有变更时,不能只看NEW.col IS NOT NULL - 比较
NULL字段必须用,比如OLD.email NEW.email,否则NULL = NULL返回UNKNOWN,条件判断失效 - 如果字段类型是
TEXT或BLOB,某些 MySQL 5.7 版本在触发器中调用CONCAT(OLD.content, ' updated')可能返回NULL—— 不是OLD失效,而是大字段上下文处理异常
跨库或复杂引用时的限制
OLD 和 NEW 只能访问当前触发器所属表的字段,不支持跨库点号语法:
- ❌ 错误写法:
OLD.other_db.users.name - ✅ 正确方式:若需关联其他库数据,得用
SELECT ... FROM other_db.table WHERE id = OLD.id显式查询(注意性能和权限) - 触发器里不能用子查询引用自身表(如
SELECT * FROM products WHERE id = OLD.id),会报错Can't update table 'products' in stored function/trigger
真正容易被忽略的是:你看到日志里某字段值为空,第一反应常是“OLD 没拿到”,但大概率是字段定义为 TEXT + MySQL 版本行为差异,或大小写/反引号漏写了——先查 SHOW CREATE TABLE 对照字段名,再试 SELECT OLD.id, OLD.name 看是否报错,比瞎猜快得多。


















