oracle触发器不支持字段级select审计,仅能捕获dml变更;真正实现“谁查了salary字段”需用dbms_fga.add_policy配置细粒度审计策略。

直接说结论:Oracle 触发器本身不支持“字段级访问审计”——它能捕获字段值的变更(DML),但无法感知 SELECT 某个字段的行为;真要监控“谁查了 salary 字段”,得用 DBMS_FGA.ADD_POLICY,不是触发器。
为什么触发器做不到字段级 SELECT 审计
触发器响应的是 DML(INSERT/UPDATE/DELETE)或 DDL/系统事件,但 SELECT 不触发任何 DML 触发器。即使你写一个 AFTER SELECT ON table_name,Oracle 会报错:PLS-00103: Encountered the symbol "SELECT" —— 语法根本不允许。
常见误操作是试图在行级触发器里判断 :NEW.salary != :OLD.salary 来“推断谁看了 salary”,这毫无意义:这个条件只反映更新行为,和查询无关。
- 触发器能做的:记录某行被
UPDATE时salary从 5000 变成 8000(值变化) - 触发器不能做的:记录用户执行
SELECT empno, salary FROM emp WHERE deptno = 10(访问行为)
真正能做字段级访问审计的只有 FGA(细粒度审计)
Oracle 9i 起内置的 DBMS_FGA 才是解决“谁查了哪些字段”的标准方案,它不依赖触发器,而是数据库内核级拦截。
关键点:
- 审计规则由
DBMS_FGA.ADD_POLICY注册,生效后无需改应用代码 -
audit_column参数明确指定字段,比如'SALARY,COMMISSION_PCT' -
audit_condition可加条件,如'deptno = 10',只审特定部门的 salary 查询 - 审计日志进
DBA_FGA_AUDIT_TRAIL,含SQL_TEXT、CLIENT_ID、OS_USER等真实上下文
示例(审计 HR.EMPLOYEES 表中对 SALARY 字段的查询):
BEGIN
DBMS_FGA.ADD_POLICY(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'AUDIT_SAL_SELECT',
audit_column => 'SALARY',
statement_types => 'SELECT',
enable => TRUE
);
END;
触发器能补什么?——仅限 DML 场景下的字段变更审计
如果你的真实需求其实是“当 salary 被 UPDATE 时,记录旧值、新值、操作人”,那触发器完全胜任,但必须是行级 + AFTER,且善用 :OLD 和 :NEW 伪记录。
注意这些坑:
- 别在
BEFORE UPDATE里插入审计日志表 —— 可能因主事务回滚导致审计记录残留(孤魂野鬼) - 避免审计所有字段:用
JSON_OBJECT('salary' VALUE :OLD.salary)显式提取,而不是TO_CLOB(:OLD)(可能报ORA-04091: table is mutating) - 如果表有大量批量 UPDATE,行级触发器会逐行执行,性能陡降;此时应评估是否改用 FGA + 应用层日志协同
-
USER函数返回当前 schema 名,不是实际登录用户;要用SYS_CONTEXT('USERENV', 'SESSION_USER')
DDL 和登录行为审计仍可依赖触发器,但和字段访问无关
有人混淆“字段级”和“对象级”。触发器确实能审计:
-
AFTER DDL ON SCHEMA:记录谁执行了ALTER TABLE emp ADD COLUMN bonus NUMBER -
AFTER LOGON ON DATABASE:记录谁、从哪台机器、用什么工具连进来
但这些审计的是“动作类型+对象名”,不是“访问了哪个字段”。把 DDL 触发器当成字段审计手段,属于典型张冠李戴。
最易被忽略的一点:FGA 策略默认不审计 SELECT * 中隐式访问的字段——必须显式列出 audit_column,否则即使语句里查了 salary,只要没写进参数列表,就不会记日志。











