After_Update触发日志记录

时间:2014-12-16 23:27:47

标签: sql sql-server sql-server-2014-express

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());

此触发器或多或少地按预期工作。它记录3个受监视字段的任何更改。但是,在此表'MY_TABLE_NAME'中插入新记录会触发上述触发器。然后,当我去看审计表'AUDIT_MY_TABLE_NAME'时,我看到这个新记录已添加到那里。唯一的区别是'audit_action'字段为空。 此插入审核表可能是由于另一个触发器更新了'MY_TABLE_NAME'after_insert中的2个字段。

我的问题是:我有点像这个触发器的工作方式。我想添加的唯一附加功能是'audit_action'读取'New record'而不是现在显示为空。请注意,我没有记录新记录但是因为After_Update触发器仍然记录它们,为什么不... 那么我必须在这个'after_update'触发器中更改什么,以便在插入新记录时,我的审计表中有'audit_action'读取'New record'?

1 个答案:

答案 0 :(得分:2)

在为单个更新编写触发器时要非常小心,但不要阻止批量更新的日志记录。如果您批量更新20条记录,您将在审计表中获得1个插入,这将是20个中随机的一个。

为了满足您的条件而不会有太大变化,您可以像这样修改触发器(这会处理批量更新):

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

但我想知道你认为发生的事情是否正确。当您更新这3个字段中的1个以上时,所有逻辑都将运行。如果您的3中有超过1列正在更新,则最后一列会获胜。我的猜测是,您的“新记录”更新实际上是正在更新的3 UPDATE列之外的字段。

这是另一种选择,我会让你选择你认为最好的选择:

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