Question

I am trying to write a trigger which would audit a table's every field - a row's old value and new value in a table. If any of the field has been modified, I need to save the fields old value and the new value along with field name in an audit table, as a new entry.

create trigger Trg_Institution_FieldAudit on Table1 AFTER UPDATE AS
DECLARE @OldName VARCHAR(30)
DECLARE @CurrentName VARCHAR(30)
DECLARE @OldId VARCHAR(30)
DECLARE @CurrentId VARCHAR(30)
DECLARE @modifiedBy VARCHAR(30)
If update(Name)
  BEGIN
    select @OldName = Name from deleted
    select @CurrentName = Name from Inserted
    select @OldId = ID from deleted
    select @currentId = ID from Inserted
    select @modifiedBy = modifiedBy from deleted
    --INSERT statement for Name field alone
    END;

This works fine for a small number of fields, but I have a lot of fields (more than 60), and I am not achieving the performance that is required, because of a lot of if conditions. Is there a better way of doing this? On top of this, there are concurrent updates that are happening to around 3 million records in this table, which makes a lot of things go wrong :(

EDIT: Only ONE row will get updated by an UPDATE statement

Was it helpful?

Solution 3

After looking for an alternative for FOR EACH in SQL Server, I found that a CURSOR can be used. It serves the purpose, but need somebody to validate this.

CREATE TRIGGER Trg_Institution_FieldAudit_1 ON dbo.Institution FOR UPDATE as 

-- DECLARE Variables

DECLARE institution_cursor CURSOR DYNAMIC FOR SELECT * FROM DELETED
OPEN institution_cursor FETCH NEXT FROM institution_cursor INTO -- @variables here
WHILE (@@FETCH_STATUS = 0)
    BEGIN    
        IF UPDATE(COL1)
        BEGIN
            INSERT INTO AuditTable VALUES (COL1, @prev, @next);
        END;

        FETCH NEXT FROM institution_cursor INTO -- @Variables here
    END
CLOSE institution_cursor
DEALLOCATE institution_cursor

OTHER TIPS

Oh my. Please avoid using a cursor whenever possible! You can easily use an insert statement with a select referencing the inserted and deleted tables. Below is a sample from one of my update triggers.

DECLARE @AuditTime DATETIME
SET @AuditTime = GetDate()

IF UPDATE([AccountManager])

    INSERT INTO Audit.AuditHistory (AuditId, AuditDate, AuditTableName,  EntityKey, AuditFieldName, OldValue, NewValue, FieldDisplayText, OldDisplayText, NewDisplayText, ModifiedBy)
    SELECT NewId(),
           @AuditTime,
           '[tblOpportunity]',
           cast(d.[GOTSID] AS varchar),
           '[AccountManager]',
           cast(d.[AccountManager] AS varchar(250)),
           cast(i.[AccountManager] AS varchar(250)),
           'Account Manager',
           isnull(cast(d.[AccountManager] AS varchar(250)), ''),
           isnull(cast(i.[AccountManager] AS varchar(250)), ''),
           isnull(i.[ModifiedBy], '')
    FROM deleted d
    INNER JOIN inserted i ON d.GOTSID = i.GOTSID 
    WHERE d.[AccountManager] <> i.[AccountManager]
    OR (d.[AccountManager] IS NOT NULL
        AND i.AccountManager IS NULL)
    OR (d.[AccountManager] IS NULL
        AND i.AccountManager IS NOT NULL)

@marc_s is right, you have to re-construct your trigger and tables. here take example.

you need to put where condition in select @OldName = Name from deleted.

e.g.-

**

CREATE TRIGGER Trg_Institution_FieldAudit ON Table1 FOR UPDATE 
AS 
DECLARE @OldName VARCHAR(30) 
DECLARE @CurrentName VARCHAR(30)
IF UPDATE (Name) 
BEGIN
       SET @OldName = Table1.Name     FROM deleted
           WHERE Table1.Name = deleted.Name;        
   SET @CurrentName = Table1.Name     FROM inserted                    
           WHERE Table1.Name = inserted.Name ; 
 --INSERT statement for old and new values.         
END 
GO**
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top