Oracle触发器中审计字段必须在BEFORE INSERT FOR EACH ROW触发器内用:new直接赋值,如:new.created_date := SYSDATE;禁止SELECT INTO或查表赋值,避免ORA-04091;需显式处理空值,统一用NVL确保非空,并注意SYSDATE时区问题。
触发器里用 :new 赋值,别用 SELECT ... INTO
oracle 触发器中修改插入数据,必须通过 :new 伪记录直接赋值,不能查表再赋值——否则会报 ora-04091: table is mutating。审计字段如 created_by、created_date 应在 before insert 阶段写入。
-
:new.created_date := SYSDATE;—— 直接赋值,安全高效 - 避免写
SELECT user, SYSDATE INTO :new.created_by, :new.created_date FROM dual;,虽语法合法但冗余且易误导 - 若需从上下文获取登录用户,优先用
USER或sys_context('USERENV', 'SESSION_USER'),而非查dba_users等视图
BEFORE INSERT 触发器必须声明为 FOR EACH ROW
没加 FOR EACH ROW 的触发器是语句级触发器,无法访问 :new,一赋值就报 ORA-04082: ORA-04082: PL/SQL functions cannot be called from SQL statements in statement-level triggers。
- 正确写法:
CREATE OR REPLACE TRIGGER tr_t1_audit BEFORE INSERT ON t1 FOR EACH ROW BEGIN ... END; - 错误写法:
CREATE OR REPLACE TRIGGER tr_t1_audit BEFORE INSERT ON t1 BEGIN ... END;—— 此时:new不可用 - 批量插入(如
INSERT /*+ APPEND */ INTO ... SELECT ...)同样触发每行,无需额外适配
空值判断要显式处理,别依赖默认值
如果表定义中审计字段允许 NULL,而触发器又没覆盖所有分支,就会漏填。比如 :new.created_by 为空时,不主动赋值,最终入库就是 NULL,审计链就断了。
- 始终对关键字段做显式赋值:
:new.created_by := NVL(:new.created_by, USER); - 避免只写
IF :new.created_by IS NULL THEN :new.created_by := USER; END IF;—— 看似安全,但万一业务逻辑提前设了空字符串'',IS NULL判定失败,仍会入库空值 - 建议统一用
NVL或COALESCE,确保字段非空
注意触发器与应用层时间戳的时区差异
SYSDATE 返回数据库服务器时区时间,而应用可能用 SYSTIMESTAMP 或客户端本地时间。如果审计要求精确到秒级且跨时区部署,SYSDATE 可能和日志、前端展示不一致。
- 若需与时区无关,改用
SYSTIMESTAMP AT TIME ZONE 'UTC'并转为TIMESTAMP字段存储 - 若字段是
DATE类型,SYSDATE是唯一选择,但得接受它不带时区信息 - 别在触发器里调用
TO_CHAR(SYSDATE, ...)存进DATE字段——类型不匹配会隐式转换,可能丢精度或出错


















