【问题标题】:two triggers on insert of same table插入同一张表时的两个触发器
【发布时间】:2010-07-16 20:39:59
【问题描述】:

这是一个非常有趣的问题。我正在使用 SQL Server 2008。 我在一张公用表上有两个触发器,比如“CommonTable”。一个触发器正在更新,另一个触发器正在插入/更新/删除。

  • 在第一个触发器“Trigger1”中,我执行检查/回滚有时会根据业务逻辑更改新插入的值。 这是示例代码

-

CREATE TRIGGER [dbo].[Trigger1] ON [dbo].[CommonTable]
FOR UPDATE
UPDATE [CommonTable] 
SET 
    [StatusCode] = 'New Value'
WHERE 
[RecId] = 'rec id value'
  • 在第二个触发器“Trigger2”中,我将新插入/删除/更新的值从“CommonTable”表存储到另一个表“CommonTable_History”,用于历史跟踪。 这是示例代码

-

CREATE TRIGGER [dbo].[Trigger2] ON [dbo].[CommonTable]
FOR INSERT, UPDATE, DELETE

--based on logic read the value from DELETED or INSERTED table and store in other table.

SELECT @RowData = (SELECT * FROM DELETED AS [CommonTable] WHERE [RecId] = @RowRecId FOR XML AUTO,                       BINARY BASE64 , ELEMENTS)

--and then insert @RowData in 'CommonTable_History' table.

在“sp_settriggerorder”的帮助下,我设置了这些触发器的执行顺序,因此首先执行“Trigger1”,然后执行“Trigger2”。

第二个触发器“Trigger2”适用于插入/删除值。如果第一个触发器“Trigger1”没有更改新插入的值,则它适用于新插入的值。

但如果在某些情况下,“Trigger1”中的插入值已更改。说 [StatusCode] = 'New Value' 并且旧值是 'Old Value' 然后“Trigger2”仍然存储“旧值”而不是“新值”。 为什么因为“Trigger1”更改了值,但该值仍未存储在数据库中,并且在“Trigger2”在 Insert 上执行之前。 现在我的要求是,我想在这里存储“新值”。

所以我想,让“Trigger2”使用“AFTER”关键字。但是“FOR”和“AFTER”的行为相同并不能解决问题。

然后我想,让“Trigger2”使用“INSTEAD OF”关键字。但是“INSTEAD OF”给出了以下错误 “不能创建 INSTEAD OF DELETE 或 INSTEAD OF UPDATE TRIGGER。这是因为该表有一个 FOREIGN KEY 与级联 DELETE 或 UPDATE。”

我无法通过级联 DELETE 或 UPDATE 删除表“CommonTable”的 FOREIGN KEY。

如果你们有任何其他替代解决方案,请告诉我。 -维克拉姆·格洛特

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    我认为您的第二个触发器需要使用实际表中的值,而不是插入/删除的表来填充日志表 - 插入/删除的将始终具有未更改的原始值,而您更改的值将出现在桌子。将第二个触发器设为“After”触发器,这样您就不必使用 sp_settriggerorder。比如这样:

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TRIGGER [dbo].[trg_Trig1] 
       ON  [dbo].[TestTable] 
       FOR INSERT
    AS 
    BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
    
        update TestTable
        set [value] = 10
        where [value] = 25
    
    END
    
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    CREATE TRIGGER [dbo].[trg_Trig2]
       ON  [dbo].[TestTable] 
       AFTER INSERT
    AS 
    BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
    
        -- Insert statements for trigger here
        insert into log_TestTable
        (id, description, [value])
        select tt.id, tt.description, tt.[value]
        from inserted i
            LEFT JOIN TestTable tt
                ON tt.id = i.id
    
    END
    

    【讨论】:

    • 我以“左”的身份离开了联接——我原本打算做其他事情。您应该改用“内部联接”。
    • 感谢大家的时间和在这里写作。我已经实现了“breitak67”提供的非常好的简单解决方案,我很惊讶为什么我没想到这一点。对于这个解决方案,我发现甚至不需要加入“插入”和“TestTable”,因为对于批量操作,我使用游标并获取行 ID,所以我可以直接从“TestTable”读取值。感谢 'HLGEM' 预见到这个问题。幸运的是,我已经在处理批量操作了。
    【解决方案2】:

    这可能不是最干净的解决方案,但您可以简单地将两个触发器合二为一吗?这样,两条 SQL 就可以知道彼此的更改。

    【讨论】:

    • 我必须同意大肠杆菌。有 2 个重叠的更新触发器听起来很麻烦。
    【解决方案3】:

    在我看来,您的第二个触发器似乎无法正常工作,因为在基于集合的操作中插入了多个记录,除非您使用的循环是触发器中的糟糕选择。先解决这个问题!

    为什么不将删除或插入的表连接到原始表并从那里获取值,而不是从删除或插入中选择 *的所有文件,如果您稍后添加其他触发逻辑不会中断。

    【讨论】:

      猜你喜欢
      • 2023-04-09
      • 1970-01-01
      • 2011-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多