行级触发器必须显式声明for each row,否则视为语句级触发器;:old和:new伪记录仅在行级触发器中可用,用于访问变更前后行数据,且不可整体判空或赋值。

行级触发器必须带 FOR EACH ROW
不加这句就是语句级触发器,哪怕你用了 :new 或 :old,Oracle 也会报错 PLS-00049: bad bind variable 'NEW'。这是最常卡住新手的第一步。
行级触发器本质是“对每一行操作都执行一次主体逻辑”,所以语法结构里 FOR EACH ROW 是强制的,不能省略,也不能写成 FOREACH ROW(少空格会报 ORA-00905: missing keyword)。
-
BEFORE和AFTER决定时机:想校验或改写即将插入/更新的数据,用BEFORE;想基于已确认变更做日志、同步或级联,用AFTER - 只在
INSERT中能读:new,DELETE中能读:old,UPDATE中两者都可用 - 不能在触发器里写
COMMIT或ROLLBACK,否则报ORA-04092: cannot COMMIT in a trigger
:new 和 :old 不是变量,是伪记录
它们不是你声明的变量,不能 DECLARE,也不能赋值给其他变量再间接使用(除非显式 SELECT ... INTO)。直接引用字段时,必须带表字段名,比如 :new.empno、:old.salary。
常见错误现象:写成 IF :new IS NOT NULL THEN ... —— 报错 PLS-00306: wrong number or types of arguments,因为 :new 是整行伪记录,不能整体判空。
- 判断某字段是否为空:用
:new.empno IS NULL,不是:new IS NULL - 更新操作中想阻止降薪:
IF :old.salary > :new.salary THEN RAISE_APPLICATION_ERROR(-20001, 'salary cannot decrease'); - 插入前生成主键:
SELECT my_seq.NEXTVAL INTO :new.id FROM dual;(仅限BEFORE INSERT)
多事件触发器要靠 INSERTING/UPDATING/DELETING 判断
一个触发器定义了 INSERT OR UPDATE OR DELETE,不代表三者逻辑能混写。必须用内置布尔变量区分当前触发动作,否则可能在 DELETE 时误读 :new.salary 导致 ORA-04098: invalid trigger 或运行时报 ORA-01403: no data found。
例如审计日志场景:
CREATE OR REPLACE TRIGGER emp_audit_trg
AFTER INSERT OR UPDATE OR DELETE ON emp
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO emp_log VALUES (USER, SYSDATE, 'I', :new.empno);
ELSIF UPDATING THEN
INSERT INTO emp_log VALUES (USER, SYSDATE, 'U', :new.empno);
ELSIF DELETING THEN
INSERT INTO emp_log VALUES (USER, SYSDATE, 'D', :old.empno);
END IF;
END;
-
INSERTING、UPDATING、DELETING是只读布尔值,不能赋值 - 不要漏掉
ELSIF分支,否则UPDATE可能被当成INSERT处理 - 注意
:new.empno在DELETE里不可用,必须用:old.empno
性能和事务边界容易被忽略
行级触发器在每行上都执行一次,如果一条 UPDATE 影响 10 万行,触发器体内的 SQL(比如 INSERT INTO log)就会执行 10 万次 —— 这比批量写日志慢一个数量级。
更隐蔽的问题是事务一致性:触发器和主 DML 同属一个事务,主语句回滚,触发器所有操作也自动回滚。但如果你在触发器里调用自治事务(PRAGMA AUTONOMOUS_TRANSACTION),就脱离了这个边界,日志会保留而主数据没写入,导致状态不一致。
- 高频写场景慎用行级日志,优先考虑物化视图日志或 CDC
- 级联更新(如
UPDATE dept SET id = :new.id WHERE id = :old.id)可能引发 mutating table 错误ORA-04091,需用复合触发器或临时表绕过 - 触发器里查本表(
SELECT ... FROM emp WHERE ...)大概率触发ORA-04091,Oracle 禁止在行级触发器中读正在修改的表











