I know this is a common problem with SQL language triggers, but I just cannot seem to get this right.
Basically I'm trying to write an "Audit Trail" trigger for all of my fields. I'm using an IF UPDATE()...
branching scheme which, as I've discovered on other SO posts, is not a reliable function.
My problem is centered around a date field that may be NULL
. I'm simply trying to write a trigger that will only write to my log table if the date field has changed (either in value or to/from NULL
)
Right now my code in this branch is as follows, and will always fire no matter if the dates didn't change (but other fields did):
IF UPDATE(DueDate)
BEGIN
DECLARE @insertedDate date
DECLARE @deletedDate date
SELECT @insertedDate = i.DueDate, @deletedDate = d.DueDate
FROM inserted i, deleted d WHERE d.DueDate <> i.DueDate
IF (@insertedDate = @deletedDate)
BEGIN
return
END
SET @IsModified = 1
SET @LogText += ('Due: ')
SELECT @TempText = '"' + ISNULL(convert(nvarchar(255), i.DueDate, 107), '[deleted]') + '" '
FROM inserted i
SET @LogText += @TempText
SET @TempText = ''
SELECT @TempText = '(was: "' + ISNULL(convert(nvarchar(255), d.DueDate, 107),'[not set]') + '"), '
FROM deleted d
SET @LogText += @TempText
SET @TempText = ''
END
Which will incorrectly produce (instead of not running at all):
Due: "Mar 04, 2020" (was: "Mar 04, 2020"),
Both @Temptext
and @LogText
are of type nvarchar(MAX)
; and @LogText
is a running string which contains the final log line to be written (and will concat through the whole procedure with +=
)
Is there anything I'm missing with how I'm constructing these conditionals? I've tried several iterations and function methods but this one is just not making sense to me…
Best Answer
This part of your trigger is totally wrong:
First error here is that you use
cross join
instead ofinner join
to joininserted
anddeleted
tables, you should join them on PK of your table.As the result, when you update more than one row at a time, you'll find rows where
d.DueDate <> i.DueDate
just because they refer to different PK values.I make you an example to show this.
Suppose your table is this one:
So you have 2 rows with 2 different dates. Now you update your PK like this:
Your trigger now goes to look
inserted
anddeleted
tables, but instead of join them onid
and have 2 result rows it makescross join
so you have 4 rows as the result of join and every row from inserted is in match withevery
row from deleted, and you find 2 rows whereinserted.DueDate <> deleted.DueDate
, exactly,20170101 <> 20170202
and20170202 <> 20170101
, even if you did not touch any DueDate at all in your update, and this is wrong.The second error is that you assign multiple values to your variables:
In my example above you assign 2 values to
@insertedDate
and 2 to@deletedDate
. You have no server error about it but the result of such assignment is unpredictable.So rewrite your trigger joining inserted and deleted on table PK and have in mind that when you assigning a set of values to a variable, you get no error but only last value will be reflected in your variable and this is not what you want