AFTER语句级触发器中不可用:NEW和:OLD,仅行级触发器(带FOR EACH ROW)才支持;AFTER行级触发器中:NEW为只读快照,可读不可改,修改无效且不报错,干预数据须用BEFORE触发器。

AFTER语句级触发器里根本就没有 NEW ——它只在行级触发器中存在,语句级触发器连 OLD 和 NEW 都不可用。
PL/SQL 中 NEW 和 OLD 仅限行级触发器
Oracle 的触发器分“语句级”和“行级”,关键区别在于是否带 FOR EACH ROW 子句:
- 不带
FOR EACH ROW:语句级触发器,只执行一次,:NEW和:OLD完全不可见,引用会直接报错PLS-00201: identifier ':NEW' must be declared - 带
FOR EACH ROW:行级触发器,每影响一行就执行一次,:NEW和:OLD才可用,且严格按事件类型生效(INSERT只有:NEW,DELETE只有:OLD)
为什么 AFTER 行级触发器能读 :NEW,但不能改
AFTER UPDATE 或 AFTER INSERT 行级触发器中,:NEW 是只读快照,可用于审计、日志或跨表操作,但赋值语句(如 :NEW.status := 'done';)会被忽略或报错:
- 语法上允许写,但运行时无效:Oracle 不报错,但修改不落地,主表数据仍是原始值
- 若字段有
NOT NULL约束且你试图设为NULL,才会在触发器执行末尾抛出ORA-01400 - 真正需要干预数据的场景(如补默认值、清洗字段),必须用
BEFORE UPDATE或BEFORE INSERT
常见误判:以为命名含 “AFTER” 就是语句级
触发器名字里带 AFTER(比如 log_after_update)不代表它是语句级——只要 DDL 中包含 FOR EACH ROW,它就是行级,:NEW 就可用;反之,哪怕叫 before_row_trigger,没写 FOR EACH ROW,照样读不到 :NEW。
- 查真实定义:运行
SELECT trigger_name, triggering_event, table_name, status FROM user_triggers WHERE trigger_name = 'YOUR_TRIGGER';,再看triggering_event是否含ROW - 更准的方式:执行
SELECT dbms_metadata.get_ddl('TRIGGER', 'YOUR_TRIGGER') FROM dual;,直接确认是否有FOR EACH ROW
真正容易被忽略的一点
即使你写了 AFTER UPDATE FOR EACH ROW,如果原 SQL 语句没真正修改任何字段(例如 UPDATE t SET name = name WHERE id = 1),Oracle 仍会触发该触发器,但 :NEW.name 和 :OLD.name 值相同,且都合法可读——这不是触发器失效,而是 Oracle 对“无变更更新”的明确支持。别靠字段值是否变化来判断触发器是否运行。

















