Reputation: 605
USE [MY_DATABASE_NAME]
GO
/****** Object: Trigger [dbo].[trg_After_Update] Script Date: 16.12.2014 23:13:53 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trg_After_Update]
ON [dbo].[MY_TABLE_NAME]
FOR UPDATE
AS
declare @FOR_DATE date;
declare @WRITTEN_ON smalldatetime;
declare @WRITTEN_BY_WHO NVARCHAR(50);
declare @REPORT nvarchar(max);
declare @HANDLED bit;
declare @HANDLED_BY NVARCHAR(50);
declare @HANDLED_WHEN datetime;
declare @COMMENT nvarchar(max);
declare @AUDIT_ACTION NVARCHAR(50);
declare @AUDIT_TIMESTAMP smalldatetime;
select @FOR_DATE = i.FOR_DATE from inserted i;
select @WRITTEN_ON = i.WRITTEN_ON from inserted i;
select @WRITTEN_BY_WHO = i.WRITTEN_BY_WHO from inserted i;
select @REPORT = i.REPORT from inserted i;
select @HANDLED = i.HANDLED from inserted i;
select @HANDLED_BY = i.HANDLED_BY from inserted i;
select @HANDLED_WHEN = i.HANDLED_WHEN from inserted i;
select @ COMMENT = i.COMMENT from inserted i;
if update(REPORT)
set @audit_action='Report change';
if update(COMMENT)
set @audit_action='Comment change';
if update(HANDLED)
set @audit_action='Handled change';
insert into AUDIT_MY_TABLE_NAME
(FOR_DATE,WRITTEN_ON,WRITTEN_BY_WHO,REPORT,HANDLED,HANDLED_BY,HANDLED_WHEN,COMMENT,USER,AUDIT_ACTION,AUDIT_TIMESTAMP)
values
(@FOR_DATE,@WRITTEN_ON,@WRITTEN_BY_WHO,@REPORT,@HANDLED,@HANDLED_BY,@HANDLED_WHEN,@COMMENT,USER_NAME(USER_ID()),@audit_action,getdate());
This trigger works more or less as expected. It logs any change to the 3 monitored fields. However, a insert of a new record into this table 'MY_TABLE_NAME' fires the above trigger. Then when I go and see the auditing table 'AUDIT_MY_TABLE_NAME' I see that this new record has been added there. The only difference is that the 'audit_action' field is empty. This insertion into the auditing table is probably caused by another trigger that updates 2 fields in 'MY_TABLE_NAME' after_insert.
My question would be this : I kind of like the way this trigger works. The only additional feature I would like added to it is that the 'audit_action' reads 'New record' instead of displaying empty now. Mind you,I am not logging new records but since this After_Update trigger logs them anyway,why not…. So what must I change in this 'after_update' trigger so that when an insert of new record occurs I have 'audit_action' reading 'New record' in my auditing table?
Upvotes: 0
Views: 79
Reputation: 3986
Be very careful when you have triggers written for single updates, but don't prevent logging for batch updates. If you update 20 records in a batch, you're going to get 1 insert into your audit table, which will be a random one of the 20.
To meet your conditions without changing much, you can modify your trigger like so (this handles batch updates):
ALTER TRIGGER [dbo].[trg_After_Update]
ON [dbo].[MY_TABLE_NAME]
FOR UPDATE
AS
INSERT INTO AUDIT_MY_TABLE_NAME (FOR_DATE,WRITTEN_ON,WRITTEN_BY_WHO,REPORT,HANDLED,HANDLED_BY,HANDLED_WHEN,COMMENT,USER,AUDIT_ACTION,AUDIT_TIMESTAMP)
SELECT i.FOR_DATE, i.WRITTEN_ON, i.WRITTEN_BY_WHO, i.REPORT, i.HANDLED, i.HANDLED_BY, i.HANDLED_WHEN, i.COMMENT, USER_NAME(USER_ID()),
CASE -- case statement is in reverse order to match your logic (bottom wins)
WHEN i.HANDLED <> d.HANDLED THEN 'Handled Changed'
WHEN i.COMMENT <> d.COMMENT THEN 'Comment Change'
WHEN i.REPORT <> d.REPORT THEN 'Report Change'
ELSE 'New Record'
END,
GETDATE()
FROM inserted i
LEFT JOIN deleted d ON i.pk = d.pk -- join on your Primary Key that doesn't change
END
But I'm wondering if what you think is going on is correct. When you update more than 1 of those 3 fields all of that logic is going to run. If more than 1 column of your 3 is being updated, the last one wins. My guess is that your "New Record" update is really a field outside of your 3 UPDATE
columns being updated.
Here's another option and I'll let you choose what you think is best:
ALTER TRIGGER [dbo].[trg_After_Update]
ON [dbo].[MY_TABLE_NAME]
FOR UPDATE
AS
INSERT INTO AUDIT_MY_TABLE_NAME (FOR_DATE,WRITTEN_ON,WRITTEN_BY_WHO,REPORT,HANDLED,HANDLED_BY,HANDLED_WHEN,COMMENT,USER,AUDIT_ACTION,AUDIT_TIMESTAMP)
SELECT i.FOR_DATE, i.WRITTEN_ON, i.WRITTEN_BY_WHO, i.REPORT, i.HANDLED, i.HANDLED_BY, i.HANDLED_WHEN, i.COMMENT, USER_NAME(USER_ID()),
CASE WHEN i.pk IS NOT NULL AND d.pk IS NULL THEN 'New Record' ELSE
CASE WHEN i.HANDLED <> d.HANDLED THEN 'Handled Changed. ' ELSE '' END +
CASE WHEN i.COMMENT <> d.COMMENT THEN 'Comment Change. ' ELSE '' END +
CASE WHEN i.REPORT <> d.REPORT THEN 'Report Change. ' ELSE '' END +
CASE WHEN i.HANDLED = d.HANDLED AND i.COMMENT = d.COMMENT AND i.REPORT = d.REPORT THEN 'Other Change.' ELSE '' END
END,
GETDATE()
FROM inserted i
LEFT JOIN deleted d ON i.pk = d.pk -- join on your Primary Key that doesn't change
END
Upvotes: 2