仅使用 SQL 数据库中的审计触发器显示编辑过的列

Display edited columns only using Audit Triggers in SQL database

我正在使用 Microsoft SSMS。

我正在尝试为我的 TestTable 创建一个审计触发器,这样当用户编辑或插入任何数据时,它都会被记录在这个 ChangeLogTable 中。下面的查询有效,我能够记录对 table.

的任何更改

但是,每当用户编辑一行时,它会记录所有内容,包括未编辑的列。例如,如果用户编辑 TestDateTimeTestStatus 将出现在 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触发器使用了一般最佳实践,例如

  1. 使用 table 个别名
  2. 使其成为set-based而不是程序
  3. 处理 null 值,因为当任何一方为 null.
  4. 时,您不能使用 <> 运算符
  5. 与使用的大小写 (lower/upper/mixed) 保持一致。
  6. 使用 semi-colons 终止语句。
  7. 架构限定您的对象 - 我假设 dbo
  8. 强制将 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;