使用Trigger在SQL Server上进行字段级审计

时间:2014-05-07 07:17:49

标签: sql-server database triggers

我正在尝试编写一个触发器来审核表的每个字段 - 一行中的旧值和表中的新值。如果任何字段已被修改,我需要将字段旧值和新值以及字段名称一起保存在审计表中,作为新条目。

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;

这适用于少数字段,但我有很多字段(超过60个),并且我没有达到所需的性能,因为有很多if条件。有没有更好的方法呢?除此之外,此表中有大约300万条记录发生并发更新,这会导致很多问题出现:(

编辑:UPDATE语句只会更新一行

3 个答案:

答案 0 :(得分:2)

哦,我的。请尽量避免使用光标!您可以轻松地使用带有select引用insert和deleted表的insert语句。以下是我的一个更新触发器的示例。

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)

答案 1 :(得分:0)

@marc_s是对的,你必须重新构建你的触发器和表。举个例子。

你需要在select中选择@OldName = Name的条件。

例如 -

**

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**

答案 2 :(得分:0)

在SQL Server中寻找FOR EACH的替代方案后,我发现可以使用CURSOR。它有用,但需要有人来验证这一点。

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