before触发器可修改:new值用于校验或预处理,after触发器只读且适合日志、同步等操作;行级触发器才可访问:old/:new;before异常会阻止dml执行,after异常可能导致副作用。

Before触发器能改数据,After触发器只能响应结果;选Before做校验或预处理,选After做日志、同步或依赖新值的操作。
Before触发器可以修改:new,After不行
行级BEFORE触发器中,你可以安全地给:new.column_name赋新值,数据库会用这个值真正插入或更新——这是它最不可替代的能力。而AFTER触发器里,:new是只读的,试图修改会报ORA-04084: cannot change NEW values for AFTER type triggers。
常见误用场景:
- 想在
AFTER INSERT里补全created_by字段——不行,得挪到BEFORE - 在
BEFORE UPDATE里统一转小写:new.email := LOWER(:new.email)——合法且常用 - 用
AFTER触发器去调用UPDATE other_table SET ...依赖刚插入的id——可以,但不能动:new
执行时机决定能否访问“旧值”和“新值”
只有行级触发器(带FOR EACH ROW)才能访问:old和:new;语句级触发器(无FOR EACH ROW)两者都不可用。但无论BEFORE还是AFTER,只要它是行级,就能用。
关键差异在于:
BEFORE UPDATE能看到修改前的:old和待生效的:new,适合做“变更检查”;
AFTER UPDATE看到的是已落地的:old和:new,适合做“变更归档”。
例如判断是否真有字段被改:
IF :old.status != :new.status THEN ... END IF;
这段逻辑放在BEFORE或AFTER都行,但放在BEFORE里还能顺便拦住非法状态跳变(比如status从'done'倒退到'pending')。
事务上下文与异常处理行为不同
BEFORE触发器属于DML语句执行流程的一部分:如果它抛出异常(比如RAISE_APPLICATION_ERROR),整个DML会回滚,且不进入AFTER阶段;AFTER触发器则是在DML成功提交(或至少写入UNDO/REDO)之后才运行,一旦它报错,整个事务仍会回滚——但它已经晚了一步,可能已产生副作用(如发了消息、写了外部表)。
所以:
- 强一致性校验(如唯一性前置检查、业务规则拦截)必须用
BEFORE - 审计日志、缓存失效、异步通知这类“尽力而为”的操作,更适合
AFTER,避免因日志失败拖垮主事务 - 不要在
AFTER里做需要强一致性的SELECT FOR UPDATE,容易遇到锁等待或死锁
别忽略语句级和行级的组合影响
你写的BEFORE INSERT可能是语句级(没加FOR EACH ROW),那就根本访问不到:new——连编译都过不去,报PLS-00049: bad bind variable 'NEW'。反过来,AFTER语句级触发器虽然不能碰:new,但能拿到SQL%ROWCOUNT,适合批量操作后的汇总统计。
典型错误配置:
CREATE OR REPLACE TRIGGER t_log_insert<br> BEFORE INSERT ON orders<br> -- 缺少 FOR EACH ROW → 无法用 :new.order_id<br> BEGIN<br> INSERT INTO log_table VALUES (:new.order_id, SYSDATE); -- 编译失败<br> END;
真正要记录每行,必须显式声明FOR EACH ROW;如果只想记“本次插了127行”,才用语句级AFTER。











