数据审计跟踪是数据库管理中保障数据操作可追溯、满足合规要求的核心功能,在SQL存储过程中实现该功能,常见的方式有两种,分别是利用触发器和利用临时表记录操作日志。

方案一:利用触发器实现数据审计跟踪
触发器的特点是当表发生增删改操作时自动触发执行,不需要在存储过程中额外编写日志写入逻辑,适合对已有存储过程改动较小的场景。
实现步骤
- 首先创建审计日志表,用于存储操作的相关信息
- 针对需要审计的业务表创建对应的触发器,在触发器中获取操作类型、操作数据、操作时间等信息写入审计日志表
- 原有的存储过程不需要修改,正常执行增删改操作即可自动生成审计记录
代码示例
首先创建审计日志表:
-- 创建审计日志表
CREATE TABLE audit_log (
log_id INT IDENTITY(1,1) PRIMARY KEY,
table_name NVARCHAR(50) NOT NULL,
operation_type NVARCHAR(10) NOT NULL,
old_data NVARCHAR(MAX),
new_data NVARCHAR(MAX),
operation_user NVARCHAR(50) NOT NULL,
operation_time DATETIME DEFAULT GETDATE()
);
然后针对用户表user_info创建更新触发器:
-- 创建更新触发器
CREATE TRIGGER trg_user_info_update
ON user_info
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
-- 获取操作人,这里从会话上下文获取,实际可根据业务调整
DECLARE @operation_user NVARCHAR(50);
SELECT @operation_user = SYSTEM_USER;
-- 插入审计记录,记录更新前后的数据
INSERT INTO audit_log (table_name, operation_type, old_data, new_data, operation_user)
SELECT 'user_info', 'UPDATE',
(SELECT * FROM deleted FOR JSON AUTO),
(SELECT * FROM inserted FOR JSON AUTO),
@operation_user;
END;
原有的更新用户存储过程不需要修改,执行更新操作时触发器会自动写入审计日志:
-- 原有更新用户存储过程
CREATE PROCEDURE proc_update_user
@user_id INT,
@user_name NVARCHAR(50),
@user_age INT
AS
BEGIN
UPDATE user_info
SET user_name = @user_name, user_age = @user_age
WHERE user_id = @user_id;
END;
方案二:利用临时表在存储过程中记录审计跟踪
这种方式需要在存储过程内部主动编写日志写入逻辑,适合需要自定义审计逻辑、或者只在特定存储过程中开启审计的场景,灵活性更高。
实现步骤
- 在存储过程内部创建临时表,用于存储当前操作产生的审计数据
- 在执行增删改操作前后,将对应的操作信息插入临时表
- 操作完成后,将临时表中的审计数据批量写入正式的审计日志表
- 最后清理临时表,避免占用资源
代码示例
以下是一个在存储过程中利用临时表实现审计的示例:
CREATE PROCEDURE proc_update_user_with_audit
@user_id INT,
@user_name NVARCHAR(50),
@user_age INT
AS
BEGIN
SET NOCOUNT ON;
-- 创建临时表存储当前操作的审计数据
CREATE TABLE #temp_audit (
old_data NVARCHAR(MAX),
new_data NVARCHAR(MAX)
);
-- 获取更新前的数据存入临时表
INSERT INTO #temp_audit (old_data)
SELECT * FROM user_info WHERE user_id = @user_id FOR JSON AUTO;
-- 执行更新操作
UPDATE user_info
SET user_name = @user_name, user_age = @user_age
WHERE user_id = @user_id;
-- 获取更新后的数据存入临时表
UPDATE #temp_audit
SET new_data = (SELECT * FROM user_info WHERE user_id = @user_id FOR JSON AUTO)
WHERE old_data IS NOT NULL;
-- 将临时表中的审计数据写入正式审计日志表
DECLARE @operation_user NVARCHAR(50) = SYSTEM_USER;
INSERT INTO audit_log (table_name, operation_type, old_data, new_data, operation_user)
SELECT 'user_info', 'UPDATE', old_data, new_data, @operation_user
FROM #temp_audit;
-- 清理临时表
DROP TABLE #temp_audit;
END;
两种方案对比
以下是两种实现方案的对比,可根据业务需求选择:
| 对比维度 | 触发器方案 | 临时表方案 |
|---|---|---|
| 侵入性 | 低,不需要修改原有存储过程 | 高,需要在存储过程中编写审计逻辑 |
| 灵活性 | 低,审计逻辑固定,难以自定义 | 高,可根据存储过程的需求自定义审计内容 |
| 性能影响 | 触发器和主操作在同一事务,可能增加事务耗时 | 可控制审计数据写入的时机,性能影响更可控 |
| 适用场景 | 全表通用审计,对已有系统改动小 | 特定存储过程的自定义审计,需要灵活控制审计逻辑 |
注意事项
- 审计日志表建议定期归档,避免数据量过大影响查询性能
- 如果审计数据包含敏感信息,需要对存储的审计数据进行加密处理
- 触发器的逻辑要尽量简单,避免触发器执行失败导致主操作回滚
- 临时表方案要注意临时表的生命周期,避免临时表残留占用资源