我已经实现了一个基于以下帖子的第一个答案提供的信息的审计跟踪框架:
SQL Server history table - populate through SP or Trigger?
最后,我实现的框架对每个表使用三个触发器,根据对表的更改插入审计信息。
我的insert和delete审计触发器相当简单。但是,update触发器要复杂得多,因为触发器必须检查每个列是否处于审计控制之下,然后根据插入列和删除列中的列值是否相等来执行插入操作,因为我不想写入不必要的审计记录。最后,我想知道是否有一种方法可以通过允许我动态执行下面的insert语句来编写存储过程,从而减少触发器中的代码量。基本上,我设想触发器使用审计控制下的每个列名触发存储过程,然后存储过程将使用列名执行下面的代码片段。目前,对于审计控制下的每一列,我都有下面的代码,不幸的是,这会导致大量冗余代码。
修改了建议更改后的触发器
CREATE TRIGGER [dbo].[Audit_Customers_Update] ON [dbo].[Customers]
FOR UPDATE AS
select FirstName,LastName into #deleted from deleted;
declare /*const*/ @TABLE_NAME sysname = '[table name]';
declare f cursor
local
forward_only
read_only
for
select c.name, quotename(c.name, '[')
from
sys.columns c
inner join sys.types t on c.system_type_id = t.system_type_id
where
c.object_id = object_id(@TABLE_NAME)
and c.is_computed = 0
and c.is_identity = 0
and t.name not in ('text', 'image', 'timestamp', 'xml')
and (substring(COLUMNS_UPDATED(), ((c.column_id - 1) / 8) + 1, 1) & power(2, (c.column_id - 1) % 8)) > 0
;
declare @field_name sysname, @field_name_sanitised sysname;
create table #results (row_id int not null,
field_name sysname not null,
oldval nvarchar(150) null,
newval nvarchar(150) null);
-- For each changed field, insert what exactly changed into #results
open f;
fetch next from f into @field_name, @field_name_sanitised;
while @@fetch_status = 0
begin
declare @query nvarchar(4000);
set @query = N'insert into #results(row_id, field_name, oldval, newval)
select d.row_id, @field_name, d.' + @field_name_sanitised + N', i.' + @field_name_sanitised + N'
from
#deleted d inner join ' + @TABLE_NAME + N' i on d.row_id = i.row_id
where
(d.' + @field_name_sanitised + N' <> i.' + @field_name_sanitised + N')
or
(case when d.' + @field_name_sanitised + N' is null then 1 else 0 end <> case when i.' + @field_name_sanitised + N' is null then 1 else 0 end);'
;
exec sp_executesql
@stmt = @query,
@params = N'@field_name sysname',
@field_name = @field_name
;
fetch next from f into @field_name, @field_name_sanitised;
end;
close f;
deallocate f;
-- Do something meaningful to #results here如何访问#结果?我必须使用游标吗?
发布于 2012-01-16 05:42:49
我们已经通过以下方式解决了这个问题。
select <list of tracked columns here> into #deleted from deleted;
declare /*const*/ @TABLE_NAME sysname = '[table name]';
declare f cursor
local
forward_only
read_only
for
select c.name, quotename(c.name, '[')
from
sys.columns c
inner join sys.types t on c.system_type_id = t.system_type_id
where
c.object_id = object_id(@TABLE_NAME)
and c.is_computed = 0
and c.is_identity = 0
and t.name not in ('text', 'image', 'timestamp', 'xml')
and (substring(COLUMNS_UPDATED(), ((c.column_id - 1) / 8) + 1, 1) & power(2, (c.column_id - 1) % 8)) > 0
;
declare @field_name sysname, @field_name_sanitised sysname;
create table #results (row_id int not null, field_name sysname not null, oldval nvarchar(150) null, newval nvarchar(150) null);
-- For each changed field, insert what exactly changed into #results
open f;
fetch next from f into @field_name, @field_name_sanitised;
while @@fetch_status = 0
begin
declare @query nvarchar(4000);
set @query = N'insert into #results(row_id, field_name, oldval, newval)
select d.row_id, @field_name, d.' + @field_name_sanitised + N', i.' + @field_name_sanitised + N'
from
#deleted d inner join ' + @TABLE_NAME + N' i on d.row_id = i.row_id
where
(d.' + @field_name_sanitised + N' <> i.' + @field_name_sanitised + N')
or
(case when d.' + @field_name_sanitised + N' is null then 1 else 0 end <> case when i.' + @field_name_sanitised + N' is null then 1 else 0 end);'
;
exec sp_executesql
@stmt = @query,
@params = N'@field_name sysname',
@field_name = @field_name
;
fetch next from f into @field_name, @field_name_sanitised;
end;
close f;
deallocate f;
-- Do something meaningful to #results here相关阅读:
发布于 2014-04-16 07:11:59
遇到了类似的问题。通过这种方式解决了问题。可能不是最优雅的解决方案,但对合规性人员有效……所以开始吧..。
基于给定的here解决方案
xml是用from从更新表的触发器中提取出来的…"OldValues“来自已删除的表,"NewValues”来自插入的表...所以最终的xml看起来像这样...
DECLARE @x XML= '<FieldData>
<UpdatedColumns>
<trType>OldValues</trType>
<ID>5</ID>
<def_label>TEST_TIE</def_label>
<def_code />
</UpdatedColumns>
<UpdatedColumns>
<trType>OldValues</trType>
<ID>4</ID>
<def_label>RP_TIE</def_label>
<def_code />
</UpdatedColumns>
<UpdatedColumns>
<trType>OldValues</trType>
<ID>3</ID>
<def_label>ERR_TIE</def_label>
<def_code />
</UpdatedColumns><UpdatedColumns>
<trType>NewValues</trType>
<ID>5</ID>
<def_label>TEST_TIE</def_label>
<def_code>A</def_code>
</UpdatedColumns>
<UpdatedColumns>
<trType>NewValues</trType>
<ID>4</ID>
<def_label>RP_TIE</def_label>
<def_code>A</def_code>
</UpdatedColumns>
<UpdatedColumns>
<trType>NewValues</trType>
<ID>3</ID>
<def_label>ERR_TIE</def_label>
<def_code>A</def_code>
</UpdatedColumns>
</FieldData>'
declare @timestamp datetime2= SYSDATETIME()
select
ID = identity(int,1,1),
T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
T.N.value('../ID[1]','nvarchar(100)') AS table_ID,
T.N.value('.', 'nvarchar(100)') as OldValue
INTO #old
from @x.nodes('//UpdatedColumns/*') as T(N)
WHERE T.N.value('../trType[1]', 'nvarchar(100)') ='OldValues'
select
ID = identity(int,1,1),
T.N.value('local-name(.)', 'nvarchar(100)') as NodeName,
T.N.value('../ID[1]','nvarchar(100)') AS Table_ID,
T.N.value('.', 'nvarchar(100)') as NewValue
into #new
from @x.nodes('//UpdatedColumns/*') as T(N)
WHERE T.N.value('../trType[1]', 'nvarchar(100)') ='NewValues'
SELECT n.table_ID, n.NodeName, o.OldValue, n.NewValue,@timestamp as transation_time FROM #new n
left outer JOIN #old o ON n.NodeName = o.NodeName AND n.ID = o.ID
WHERE isnull(o.[OldValue],'') <> isnull(n.[newValue],'') AND n.NodeName <> 'trType'
DROP TABLE #new,#old
GOhttps://stackoverflow.com/questions/8873335
复制相似问题