结论:仅当dbms_fga无法捕获old/new值时才用after row触发器,必须显式引用:old/:new、处理null、避免mutating错误、启用自治事务、合理设计审计表与日志逻辑。
直接说结论:只在必须记录 :old 和 :new 值(比如薪资从 8000 改成 12000)、且 dbms_fga 不支持新旧值捕获时,才用触发器;否则优先走 fga 或统一审计策略。
必须用 AFTER ROW 触发器 + 显式引用 :OLD/:NEW
想拿到字段变更前后的具体值,BEFORE UPDATE 拿不到完整的 :NEW,AFTER STATEMENT 根本不暴露行级数据。只有 AFTER UPDATE FOR EACH ROW 才能安全访问这两个伪记录。
- 写法必须是
:OLD.salary和:NEW.salary,不能写成OLD.salary(少冒号会报错) - 如果字段可能为 NULL,比较前先用
NVL(:OLD.salary, -1) != NVL(:NEW.salary, -1),避免 NULL != NULL 返回 FALSE 导致漏记 - 不要在触发器里做
SELECT * FROM employees WHERE emp_id = :NEW.emp_id—— 这会触发 ORA-04091 mutating table 错误
PRAGMA AUTONOMOUS_TRANSACTION 是硬性要求
审计日志插入必须和主事务解耦,否则主事务一回滚,审计记录就跟着消失,等于没记。
- 开头必须加
PRAGMA AUTONOMOUS_TRANSACTION;,紧接着就要COMMIT;(或ROLLBACK;) - 别把审计表建在和业务表同一个表空间还用相同用户管理——万一审计表 DDL 失败(比如字段长度不够),整个 UPDATE 就失败
- 建议审计字段用
CLOB存 JSON,比如JSON_OBJECT('salary' VALUE :OLD.salary, 'emp_id' VALUE :OLD.emp_id),别用TO_CLOB(:OLD)(会触发 mutating 错误)
避免无意义日志刷屏和权限陷阱
生产环境最常踩的不是语法坑,而是逻辑和权限设计疏漏。
- 加空值/等值判断:
IF :OLD.salary != :NEW.salary THEN ... END IF;,否则每次 UPDATE 都记一条“没变”的日志 - 触发器本身没有开关机制,禁用得靠
ALTER TRIGGER xxx DISABLE;但很多人改表结构前忘了这步,结果触发器拦住ALTER TABLE,导致上线卡住 -
AFTER CREATE ON DATABASE这类系统级触发器需要ADMINISTER DATABASE TRIGGER权限,普通 DBA 角色默认没有,得单独授权
真正难的不是写出来,而是想清楚:这个字段是否真需要精确到值的审计?有没有被 FGA 覆盖?触发器一旦启用,就再也无法“静默关闭”——它始终在后台运行,哪怕你忘了它存在。











