必须通过在基表上创建触发器并写入审计表来监控视图操作,因视图本身不存储数据、不支持触发器,且dml实际作用于基表。

SQL视图本身不记录日志,必须靠触发器+审计表实现
视图只是查询的封装,它不存储数据,也不参与写操作——所以 CREATE VIEW 后你对视图执行 INSERT、UPDATE 或 DELETE,实际影响的是底层基表,视图自身完全无感知。想“监控视图操作”,本质是监控对视图所依赖表的 DML 行为,而触发器是唯一可控入口。
触发器必须建在基表上,不是视图上
直接在视图上建 BEFORE INSERT 触发器会报错:ERROR: cannot create trigger on view。PostgreSQL 和 MySQL 都不支持视图触发器(SQL Server 的 INSTEAD OF 触发器除外,但那是另一套逻辑)。所以真实路径是:
- 找出视图定义中涉及的所有基表(用
pg_views或INFORMATION_SCHEMA.VIEWS查) - 在每个可修改的基表上创建
AFTER INSERT/AFTER UPDATE/AFTER DELETE触发器 - 触发器内判断是否由该视图引发(可通过
CURRENT_SETTING('app.view_context')传上下文,或靠业务字段标记) - 把关键信息(用户、时间、语句类型、主键值、旧/新值)写入独立审计表
审计表设计要避开常见陷阱
很多人直接把整行 NEW 或 OLD 转成 JSON 存进 TEXT 字段,结果查起来慢、没法索引、还容易被截断。更稳妥的做法是:
- 审计表字段尽量平铺:比如
audit_id、table_name、op_type('I'/'U'/'D')、pk_value、changed_fields(JSONB或HSTORE)、user_name、created_at - 避免在触发器里调用
CURRENT_USER—— 它可能返回连接池用户名(如pgbouncer),改用SESSION_USER或应用层显式传参 - 不要在触发器里做复杂计算或远程调用,否则拖慢主事务;审计失败不能阻塞原操作,建议用异步队列或写入失败时只记错误日志
MySQL 与 PostgreSQL 在触发器审计上的关键差异
MySQL 的触发器无法访问当前 SQL 语句文本,也拿不到执行用户全名(USER() 返回 user@host);PostgreSQL 则可通过 current_setting('application_name', true) 或自定义 GUC 参数传递视图标识,还能用 pg_stat_activity 关联会话。另外:
- MySQL 触发器不支持
OLD.*和NEW.*在DELETE中同时引用,OLD是唯一可用的 - PostgreSQL 的
EXECUTE动态语句在触发器中受限,不能直接拼接并运行任意 SQL,但可用json_build_object构造变更快照 - 两者都禁止在触发器里开启新事务,所以审计写入必须和主事务同属一个事务块,崩溃时自动回滚
真正难的不是建触发器,而是厘清“谁在什么上下文里改了什么”——视图可能被多个应用、多种方式(ORM、直连、ETL 工具)访问,单靠数据库层很难 100% 追溯源头。如果业务允许,优先在应用层埋点,数据库触发器只作兜底。










