Reputation: 87
I am using Microsoft SSMS.
I am trying to create an audit Trigger for my TestTable
so that when a user edits or inserts any data, it will be recorded in this ChangeLogTable
. The query below works and I am able to record any changes to the table.
However, whenever a user edits a row, it will record everything including the columns that are not edited. For example, if a user edits the TestDateTime
, the TestStatus
will appear in the ChangeLogTable
even though there were no changes to it.
How do I change the query so that only the edited columns are recorded?
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
Upvotes: 0
Views: 119
Reputation: 27202
The problem is you have confused your join on
clause with your where
clause. Because you had for example inserted.TestStatus<> deleted.TestStatus
as part of your on
clause, you never matched a Deleted
row if the data changed. Whereas what you wanted was to match the row on ID and then check whether the value had changed.
Note I've re-written the trigger using general best practices for clarity e.g.
null
values, because you can't use the <>
operator when either side is null
.dbo
.varchar
for the datetime
column as its better to convert explicitly rather than implicitly.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;
Upvotes: 1