Reputation: 3694
Case
I have two tables with following structures (I removed 34 other columns that were not related to question)
Table1
------
Id
LastAccessed
Data1
Data2
Table1_History
------
_Id
Action
Id
LastAccessed
Data1
Data2
Every time a user reads the record (with specific procedure) the LastAccessed
timestamp will change.
I attached a trigger to Table1
which copies the record to history table if Data1
or Data2
(or in my case any other column changes); However I don't want a copy or record in case LastAccessed
changes.
Here is my trigger
CREATE TRIGGER [TRIGGER!]
ON TABLE1
AFTER UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
IF NOT UPDATE([LastAccessed]) BEGIN
IF EXISTS(SELECT * FROM inserted) BEGIN
INSERT INTO [Table1_Hist](
Action, Id, LastAccessed, Data1, Data2
) SELECT
'EDIT', Id, LastAccessed, Data1, Data2
FROM deleted
END ELSE BEGIN
INSERT INTO [Table1_Hist](
Action, Id, LastAccessed, Data1, Data2
) SELECT
'DELETE', Id, LastAccessed, Data1, Data2
FROM deleted
END
END
END
This trigger will not copy row if LastAccessed and Data1 both change (which I want to). to solve this issue I can change the IF
statement to
IF UPDATE(Id) OR UPDATE(Data1) OR UPDATE(Data2) ... BEGIN
In this case it will work as intended
Question:
As I have 34 columns in my table it is not easy to specify every column in IF
statement.
Is there any easier way of doing this?
Upvotes: 1
Views: 4256
Reputation: 451
CREATE TRIGGER [TRIGGER!]
ON TABLE1
AFTER UPDATE
AS
BEGIN
INSERT INTO Table1_Hist (
Action
Id
LastAccessed
Data1
Data2
)
SELECT 'UPDATE',d.ID,d.LastAccessed,d.Data1,d.Data2, etc., etc.
FROM DELETED d
INNER JOIN INSERTED i ON d.id = i.id
WHERE
COALESCE(d.data1,'') <> COALESCE(i.data1,'') OR
COALESCE(d.data2,'') <> COALESCE(i.data2,'') OR
<repeat for every attribute EXCEPT LastAccessed>
END
This will also work for inserts or updates of multiple rows, whereas your method would potentially insert rows where the data did not actually change.
Also, you want to have separate UPDATE and DELETE triggers because your query should just dump the full row set from DELETED in case of an actual DELETE.
Upvotes: 0
Reputation: 586
Hopefully someone has a more elegant answer than this, but worst case you can generate that ugly IF statement for all 34 columns with a simple query:
SELECT CASE WHEN column_id = 1 THEN 'IF ' ELSE 'OR ' END + 'UPDATE(' + name + ')'
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[dbo].[Table1]')
AND name <> 'LastUpdated'
ORDER BY column_id
Then you can just cut-and-paste the results for your IF statement... not an elegant solution, but it's quicker than typing it all by hand.
Upvotes: 3