new和old仅在mysql行级触发器中可用,代表操作行的只读伪记录;insert中仅new可用,delete中仅old可用,update中两者皆可;字段类型与原表一致,虚拟列不出现。

NEW 和 OLD 只能在行级触发器中使用
MySQL 不支持语句级触发器,所以 NEW 和 OLD 在任何触发器里都只能用于 FOR EACH ROW 场景。如果你写了 FOR EACH STATEMENT(语法错误),或者漏写 FOR EACH ROW,整个触发器创建会失败,报错类似 ERROR 1363: There is no NEW row in on INSERT statement 或 ERROR 1362: Updating of NEW row is not allowed in after trigger。
它们不是变量,而是“伪记录”(transition variables)——每个代表当前被操作的那一行的快照,结构和原表一致,字段名可直接通过点号访问。
INSERT 触发器里只能用 NEW
OLD 在 INSERT 中完全不可用,哪怕只是读取也会报错:ERROR 1363: There is no OLD row in on INSERT statement。而 NEW 包含即将插入的整行数据,字段值可读可写(仅限 BEFORE INSERT)。
-
BEFORE INSERT:可以修改NEW.column,比如自动填充时间戳、生成 UUID、校验并重写数值 -
AFTER INSERT:NEW.column只读,值已落库,可用于日志记录或关联更新其他表 - 常见误用:在
AFTER INSERT里给NEW.id = LAST_INSERT_ID()赋值——无效,NEW.id已确定,赋值无效果
UPDATE 触发器里 NEW 和 OLD 都可用,但用途截然不同
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,但触发器仍会执行——别依赖“字段变了才触发”,要自己比对
DELETE 触发器里只能用 OLD
NEW 在 DELETE 中非法,尝试引用会报 ERROR 1363: There is no NEW row in on DELETE statement。OLD 是唯一能拿到被删行数据的方式,且仅在 BEFORE DELETE 和 AFTER DELETE 中都有效(只读)。
-
BEFORE DELETE:适合做外键软约束检查,比如确认子表无关联记录再允许删除 -
AFTER DELETE:适合归档或异步清理,例如把OLD行插入到历史表:INSERT INTO goods_history SELECT OLD.* - 关键限制:不能在
BEFORE DELETE中修改OLD.column——赋值无效,也不会影响删除行为
最常被忽略的一点是:NEW 和 OLD 的字段类型、NULL 性、默认值行为,完全继承原表定义。如果某列允许 NULL,但触发器里没显式处理,NEW.col IS NULL 就可能意外为 true;如果用了生成列或虚拟列,它们不会出现在 NEW/OLD 中——这些细节不报错,但逻辑会静默失效。











