仅使用 SQL 数据库中的审计触发器显示编辑过的列
Display edited columns only using Audit Triggers in SQL database
我正在使用 Microsoft SSMS。
我正在尝试为我的 TestTable
创建一个审计触发器,这样当用户编辑或插入任何数据时,它都会被记录在这个 ChangeLogTable
中。下面的查询有效,我能够记录对 table.
的任何更改
但是,每当用户编辑一行时,它会记录所有内容,包括未编辑的列。例如,如果用户编辑 TestDateTime
,TestStatus
将出现在 ChangeLogTable
中,即使它没有任何更改。
如何更改查询以便仅记录编辑的列?
Create trigger test_Audit
on TestTable
after Insert, update
not for replication
as
Declare @operation char(10)
if exists(select * from deleted)
set @operation = 'Edit'
else
set @operation = 'Insert'
if UPDATE (TestDateTime)
insert ChangeLog
(TableName, ModifiedBy, ChangeLogDateTime, AuditAction, WorkScheduleID, ChangedColumn , OldValue, NewValue)
select
'TestTable', SUSER_SNAME(), GETDATE(), @operation, inserted.TestID,
'TestDateTime', Deleted.TestDateTime, Inserted.TestDateTime
from inserted
left outer join deleted
on inserted.TestID = deleted.TestID
and inserted.TestDateTime <> deleted.TestDateTime
if UPDATE (TestStatus)
insert ChangeLog
(TableName, ModifiedBy, ChangeLogDateTime, AuditAction, WorkScheduleID, ChangedColumn, OldValue, NewValue)
select
'TestTable', SUSER_SNAME(), GETDATE(), @operation, inserted.TestID,
'TestStatus', Deleted.TestStatus, Inserted.TestStatus
from inserted
left outer join deleted
on inserted.TestID = deleted.TestID
and inserted.TestStatus<> deleted.TestStatus
if UPDATE (StaffID)
insert AuditLog
(TableName, ModifiedBy, AuditDateTime, AuditAction, ID, ChangedColumn, OldValue, NewValue)
select
'TestTable', SUSER_SNAME(), GETDATE(), @operation, inserted.TestID,
'StaffID', OSN.StaffName, NSN.StaffName
from inserted
left outer join deleted
on inserted.TestID= deleted.TestID
and inserted.StaffID <> deleted.StaffID
-- Fetch Staff Name
left outer join dbo.Staff OSN
on deleted.StaffID = OSN.StaffID
join dbo.Staff NSN
on inserted.StaffID = NSN.StaffID
问题是您将 join on
子句与 where
子句混淆了。例如,因为您将 inserted.TestStatus<> deleted.TestStatus
作为 on
子句的一部分,所以如果数据更改,您永远不会匹配 Deleted
行。而您想要的是匹配 ID 上的行,然后检查值是否已更改。
请注意,为了清楚起见,我re-written触发器使用了一般最佳实践,例如
- 使用 table 个别名
- 使其成为set-based而不是程序。
- 处理
null
值,因为当任何一方为 null
. 时,您不能使用 <>
运算符
- 与使用的大小写 (lower/upper/mixed) 保持一致。
- 使用 semi-colons 终止语句。
- 架构限定您的对象 - 我假设
dbo
。
- 强制将
datetime
列转换为 varchar
,因为显式转换比隐式转换更好。
alter trigger dbo.test_Audit
on dbo.TestTable
after insert, update
not for replication
as
begin
set nocount on;
declare @operation char(10) = case when exists (select * from deleted) then 'Edit' else 'Insert' end;
insert ChangeLog (TableName, ModifiedBy, ChangeLogDateTime, AuditAction, WorkScheduleID, ChangedColumn , OldValue, NewValue)
select
'TestTable', suser_sname(), getdate(), @operation, I.TestID,
'TestDateTime', convert(varchar(21),D.TestDateTime), convert(varchar(21),I.TestDateTime)
from inserted I
left outer join deleted D on I.TestID = D.TestID
where coalesce(I.TestDateTime,'1 jan 1900') <> coalesce(D.TestDateTime,'1 jan 1900')
and update(TestDateTime)
union all
select
'TestTable', suser_sname(), getdate(), @operation, I.TestID,
'TestStatus', D.TestStatus, I.TestStatus
from inserted I
left outer join deleted D on I.TestID = D.TestID
where coalesce(I.TestStatus,'') <> coalesce(D.TestStatus,'')
and update(TestStatus)
end;
我正在使用 Microsoft SSMS。
我正在尝试为我的 TestTable
创建一个审计触发器,这样当用户编辑或插入任何数据时,它都会被记录在这个 ChangeLogTable
中。下面的查询有效,我能够记录对 table.
但是,每当用户编辑一行时,它会记录所有内容,包括未编辑的列。例如,如果用户编辑 TestDateTime
,TestStatus
将出现在 ChangeLogTable
中,即使它没有任何更改。
如何更改查询以便仅记录编辑的列?
Create trigger test_Audit
on TestTable
after Insert, update
not for replication
as
Declare @operation char(10)
if exists(select * from deleted)
set @operation = 'Edit'
else
set @operation = 'Insert'
if UPDATE (TestDateTime)
insert ChangeLog
(TableName, ModifiedBy, ChangeLogDateTime, AuditAction, WorkScheduleID, ChangedColumn , OldValue, NewValue)
select
'TestTable', SUSER_SNAME(), GETDATE(), @operation, inserted.TestID,
'TestDateTime', Deleted.TestDateTime, Inserted.TestDateTime
from inserted
left outer join deleted
on inserted.TestID = deleted.TestID
and inserted.TestDateTime <> deleted.TestDateTime
if UPDATE (TestStatus)
insert ChangeLog
(TableName, ModifiedBy, ChangeLogDateTime, AuditAction, WorkScheduleID, ChangedColumn, OldValue, NewValue)
select
'TestTable', SUSER_SNAME(), GETDATE(), @operation, inserted.TestID,
'TestStatus', Deleted.TestStatus, Inserted.TestStatus
from inserted
left outer join deleted
on inserted.TestID = deleted.TestID
and inserted.TestStatus<> deleted.TestStatus
if UPDATE (StaffID)
insert AuditLog
(TableName, ModifiedBy, AuditDateTime, AuditAction, ID, ChangedColumn, OldValue, NewValue)
select
'TestTable', SUSER_SNAME(), GETDATE(), @operation, inserted.TestID,
'StaffID', OSN.StaffName, NSN.StaffName
from inserted
left outer join deleted
on inserted.TestID= deleted.TestID
and inserted.StaffID <> deleted.StaffID
-- Fetch Staff Name
left outer join dbo.Staff OSN
on deleted.StaffID = OSN.StaffID
join dbo.Staff NSN
on inserted.StaffID = NSN.StaffID
问题是您将 join on
子句与 where
子句混淆了。例如,因为您将 inserted.TestStatus<> deleted.TestStatus
作为 on
子句的一部分,所以如果数据更改,您永远不会匹配 Deleted
行。而您想要的是匹配 ID 上的行,然后检查值是否已更改。
请注意,为了清楚起见,我re-written触发器使用了一般最佳实践,例如
- 使用 table 个别名
- 使其成为set-based而不是程序。
- 处理
null
值,因为当任何一方为null
. 时,您不能使用 - 与使用的大小写 (lower/upper/mixed) 保持一致。
- 使用 semi-colons 终止语句。
- 架构限定您的对象 - 我假设
dbo
。 - 强制将
datetime
列转换为varchar
,因为显式转换比隐式转换更好。
<>
运算符
alter trigger dbo.test_Audit
on dbo.TestTable
after insert, update
not for replication
as
begin
set nocount on;
declare @operation char(10) = case when exists (select * from deleted) then 'Edit' else 'Insert' end;
insert ChangeLog (TableName, ModifiedBy, ChangeLogDateTime, AuditAction, WorkScheduleID, ChangedColumn , OldValue, NewValue)
select
'TestTable', suser_sname(), getdate(), @operation, I.TestID,
'TestDateTime', convert(varchar(21),D.TestDateTime), convert(varchar(21),I.TestDateTime)
from inserted I
left outer join deleted D on I.TestID = D.TestID
where coalesce(I.TestDateTime,'1 jan 1900') <> coalesce(D.TestDateTime,'1 jan 1900')
and update(TestDateTime)
union all
select
'TestTable', suser_sname(), getdate(), @operation, I.TestID,
'TestStatus', D.TestStatus, I.TestStatus
from inserted I
left outer join deleted D on I.TestID = D.TestID
where coalesce(I.TestStatus,'') <> coalesce(D.TestStatus,'')
and update(TestStatus)
end;