INSERT触发器中只能使用NEW,OLD不可用;DELETE触发器中仅OLD可用;UPDATE触发器中NEW和OLD均可访问,但OLD始终只读,NEW在BEFORE中可写、AFTER中只读。

INSERT触发器里只能用NEW,不能碰OLD
在INSERT语句触发的触发器中,OLD根本不可用——MySQL会直接报错ERROR 1363 (HY000): There is no OLD row in on INSERT trigger。因为插入前不存在“旧行”,OLD无从指向。
你只能读写NEW.column_name,比如做数据清洗或默认值补全:
CREATE TRIGGER t_before_insert_user
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
IF NEW.created_at IS NULL THEN
SET NEW.created_at = NOW();
END IF;
END;-
NEW可读可写,适合校验、修正、填充字段 - 若在
AFTER INSERT中尝试修改NEW,MySQL会忽略(不报错但无效) - 别在
INSERT触发器里声明OLD.id这类引用,语法错误立刻出现
DELETE触发器里只有OLD可用,NEW是非法的
DELETE触发器中NEW完全禁止使用,否则触发ERROR 1363 (HY000): There is no NEW row in on DELETE trigger。删除操作不产生新行,NEW没有语义基础。
典型用途是归档或日志记录:
CREATE TRIGGER t_after_delete_order AFTER DELETE ON orders FOR EACH ROW BEGIN INSERT INTO order_archive SELECT OLD.*; END;
-
OLD只读,不能赋值(如SET OLD.status = 'archived'会报错) - 可以安全地把
OLD.*整行插入到归档表,前提是字段顺序和类型兼容 - 若归档表有自增主键,需显式排除该列,否则可能冲突
UPDATE触发器中OLD和NEW都可用,但权限不同
UPDATE是唯一允许同时访问OLD和NEW的场景,但二者行为差异极大:
-
OLD.column_name只读,任何赋值操作都会报ERROR 1362 (HY000): Updating of OLD row is not allowed in after trigger -
NEW.column_name在BEFORE UPDATE中可写,在AFTER UPDATE中只读 - 常见误用:在
AFTER UPDATE里试图SET NEW.amount = OLD.amount * 1.1——语法通过但实际无效
一个实用例子:仅当金额变化时才更新修改时间戳:
CREATE TRIGGER t_before_update_payment
BEFORE UPDATE ON payments
FOR EACH ROW
BEGIN
IF OLD.amount != NEW.amount THEN
SET NEW.updated_at = NOW();
END IF;
END;跨事件混用NEW/OLD会直接中断执行
触发器体内的NEW或OLD引用不是“可选变量”,而是由触发事件强约束的上下文对象。MySQL在解析阶段就检查合法性,不等到运行时。
- 哪怕只是
SELECT OLD.id INTO @x出现在INSERT触发器里,也会拒绝创建 - 存储过程里调用
INSERT语句,不会“继承”外部作用域的OLD或NEW——它们只存在于触发器作用域内 - 触发器嵌套(如A触发B)时,每个触发器的
OLD/NEW各自独立,互不影响
真正容易被忽略的是:这些限制和事务隔离无关,也不受SQL_MODE影响——它是语法层硬性规则,绕不开也妥协不了。


















