【问题标题】:SQL Server 2008 created/last updated trigger issueSQL Server 2008 创建/上次更新触发器问题
【发布时间】:2013-03-19 09:30:34
【问题描述】:

我正在使用此触发器来填充表格中的CreatedDateLastUpdated 列(datetime 类型):

CREATE TRIGGER trCreatedDate ON [LasMTest]
FOR INSERT 
AS
    UPDATE [LasMTest] 
    SET [LasMTest].Created = getdate(),
        [LasMTest].LastModified = getdate()
    FROM [LasMTest] 
    INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID]
GO

当我查看表格时,日期只差了几分之一秒。

Created LastModified    ID
2013-03-19 09:24:32.920 2013-03-19 09:24:32.930 4
2013-03-19 09:26:39.890 2013-03-19 09:26:39.900 5

如何修改触发器以使它们都是准确的时间?

【问题讨论】:

    标签: sql sql-server-2008 datetime triggers


    【解决方案1】:

    是您的两个触发器的交互导致了问题。

    如果您将两列都设置为 defaultgetdate() 并放弃您的 insert 触发器,它应该可以工作 - INSERT 也不会导致 UPDATE

    另一种方法是将您的INSERT 触发器编写为执行INSERT 而不是UPDATEINSTEAD OF 触发器(因此也可以避免UPDATE 触发器触发)。


    如果您确实想将其编写为INSTEAD OF 触发器,则类似于:

    CREATE TRIGGER trCreatedDate ON [LasMTest]
    INSTEAD OF INSERT 
    AS
        INSERT INTO LasMTest (/* Column List */,Created,LastModified)
        SELECT /* Column List */,getdate(),getdate() from inserted
    GO
    

    INSERTs 在INSTEAD OF 触发器中的触发表中不会(谢天谢地)导致触发器被递归触发。如果IDIDENTITY 列,那么它应该出现在上面的列列表中(尚未生成)。

    【讨论】:

    • INSTEAD OF 的语法是什么?
    • 创建和删除插入触发器的默认约束工作得很好。谢谢
    【解决方案2】:

    试试这个:

    CREATE TRIGGER trCreatedDate ON [LasMTest]
    
    FOR INSERT 
    
    AS
    Declare @CurrentDate Datetime
    Set @CurrentDate=getdate()
    UPDATE [LasMTest] SET [LasMTest].Created=@CurrentDate,[LasMTest].LastModified=@CurrentDate
    
    FROM [LasMTest] INNER JOIN Inserted ON [LasMTest].[ID]= Inserted.[ID]
    
    GO
    

    【讨论】:

    • 我也试过了,但效果不太好,可能是因为我有另一个 UPDATE 触发器,我认为这是造成差异的原因。 CREATE TRIGGER trLastModifiedDate ON [LasMTest] FOR UPDATE AS UPDATE [LasMTest] SET [LasMTest].LastModified=getdate() FROM [LasMTest] INNER JOIN Inserted ON [LasMTest].[ID]= Inserted.[ID] 基本上似乎实际上在我执行 INSERT 时触发,我希望它仅在更新行而不是创建行时触发,如何更改?
    【解决方案3】:

    您基本上希望在从 INSERT 进行更新时避免 UPDATE 触发器。这称为Nested Triggers。一种简单的解决方案是使用 CONTEXT_INFO() 与嵌套的 UPDATE 触发器代码进行通信,您已经在 INSERT 触发器中,因此它会自我抑制:

    CREATE TRIGGER trCreatedDate ON [LasMTest]
    FOR INSERT 
    AS
        SET CONTEXT_INFO 0xDEADBEEF;
        UPDATE [LasMTest] 
        SET [LasMTest].Created = getdate(),
            [LasMTest].LastModified = getdate()
        FROM [LasMTest] 
        INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
        SET CONTEXT_INFO NULL;
    GO
    
    CREATE TRIGGER trModifiedDate ON [LasMTest]
    FOR UPDATE
    AS
        DECLARE @context varbinary(128);
        SET @context = CONTEXT_INFO();
        IF @context is NULL
        BEGIN
           UPDATE [LasMTest] 
              SET [LasMTest].LastModified = getdate()
            FROM [LasMTest] 
            INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
        END
    GO
    

    但是这种方法是脆弱的。异常可以保留 context_info 设置并抑制会话中的所有后续 UPDATE 触发器。这需要添加 TRY/CATCH 块。而且您总是冒着应用程序将CONTEXT_INFO 用于其自身 目的并破坏您的计划的风险。

    另一个解决方案是使 UPDATE 触发器更智能。它可以检查 UPDATE 触发器中的 UPDATE(Created) 并在 Created 列被修改时禁止任何操作。这是按惯例工作的,因为您知道更新 Created 列的唯一位置是 INSERT 触发器:

    CREATE TRIGGER trModifiedDate ON [LasMTest]
    FOR UPDATE
    AS
        IF NOT UPDATE(Created)
        BEGIN
           UPDATE [LasMTest] 
              SET [LasMTest].LastModified = getdate()
            FROM [LasMTest] 
            INNER JOIN Inserted ON [LasMTest].[ID] = Inserted.[ID];
        END
    GO
    

    【讨论】:

      猜你喜欢
      • 2014-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-07
      相关资源
      最近更新 更多