【问题标题】:Prevent triggering multiple triggers at same time on table update防止在表更新时同时触发多个触发器
【发布时间】:2021-11-05 20:02:09
【问题描述】:

在 SQL 服务器中,我有一个表 X 和一个表 Y。 两者的主键相同,例如 Z。 我创建了一个触发器,如果​​在 X 中创建/删除/更新任何内容,那么

  1. 如果 Y for Z 中不存在该条目,则创建一个新条目。
  2. 如果 Y 中存在针对 Z 的条目,则更新该行。

我运行了这样的查询:

Delete FROM Table2 where TId = 1;

此查询删除了 10 行,同时运行了 10 个触发器。由于所有触发器都并行运行,因此每个触发器都执行了 else 块,因为最初在 Y 中没有针对 Z 的条目,并且由于所有都在运行,所以它们不会在 Y 中找到行。因此,在 Y 表中创建了 10 行。

我希望只创建 1 个条目,而其他触发器应该更新该条目。

例如,我有表格:

Table1(TId PRIMARY KEY, C12, C13);

Table2(C21 PRIMARY KEY, TId FOREIGN KEY(Table1, C11), C23);

Table3(TId PRIMARY KEY, C32, C33);

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trg1] ON  [dbo].[TABLE2] AFTER DELETE
        AS
        BEGIN
        SET NOCOUNT ON
            IF EXISTS (SELECT * FROM Deleted D )
                    BEGIN
                        IF EXISTS (Select * from [dbo].[Table3] where TId in (Select TId from Deleted D) )
                            BEGIN
                                Update [dbo].[Table3] SET C32 = 1 where TId in (Select TId from Deleted D);
                        END
                        ElSE
                            BEGIN
                            INSERT INTO [dbo].[Table3]
                                (
                                    TId,
                                    C32,
                                    C33
                                )
                            SELECT  TId, 
                                    3,
                                    GETUTCDATE()
                            FROM Deleted D WHERE TId is not null
                            SET NOCOUNT OFF
                        END

            END
        SET NOCOUNT OFF
    END
            
PRINT '';
PRINT 'End of script';
PRINT ' --- // ---';

【问题讨论】:

  • 您需要向minimal reproducible example 提供示例数据、预期结果和触发代码。
  • 你误诊了你的情况。在 SQL Server 中,触发器每 受影响的行执行一次,所以不,您没有同时运行 10 个触发器。更有可能是您的触发器被错误地写入假设它一次只处理一行。
  • @DaleK 我已经用一个例子更新了这个问题。

标签: sql sql-server database tsql triggers


【解决方案1】:

您的整个触发器的代码应该是这样的:

MERGE INTO Table3 t
USING (SELECT TId,CASE WHEN COUNT(*) > 1 THEN 1 ELSE 3 END as C32
       FROM deleted
       GROUP BY TId) u
ON t.TId = u.TId
WHEN MATCHED THEN UPDATE SET C32 = 1
WHEN NOT MATCHED AND u.TId is not null THEN
    INSERT (TId,C32,C33) VALUES (u.TId, u.C32, GETUTCDATE());

这将插入一个新行,但决定是否将C32 设置为13,这取决于DELETED 中有多少行用于相同的TId,只需将C32 更新为1 如果一行已经存在。

这都是关于在集合中思考。您没有考虑到 deleted 可能包含多行,其中一些或全部可能具有相同的 TId 值。您不会编写只能对 deleted1 中的所有行做出单一决定的 IF/ELSE 块。


1例如如果删除总共包含 4 行,TId 6 为 2 行,TId 8 为 2 行,Table3 包含TId 6 行但TId 8 没有行,您的触发器会找到一些匹配Table3 中的行并刚刚执行UPDATE

【讨论】:

  • 如果我运行 Delete FROM Table2 where TId = 1;并且从具有相同 TId 的 Table2 中删除了 10 行,并且 Table3 中没有 Tid = 1 的行。它是否只创建一行并为其他触发器更新它。如果是这样,如果我运行这个,表格会有任何锁定吗?
  • @akshit - 您一直认为多个触发器正在运行。他们不是。该触发器代码将运行一次,其deleted 表包含所有10 行。如您所见,USING 中的子选择将(通过GROUP BY)将所有这些行减少到一行(每个TId)以尝试匹配Table3 中的行。
  • 您能帮我理解一下,当我使用之前的触发器和相同的场景运行时,为什么当时它创建了 10 行?如果这是一个愚蠢的问题,我很抱歉。我最近才开始研究这个。
  • @akshit - 因为inserted 中有 10 行。它们都不匹配Table3 中的一行。然后,您从inserted 执行了INSERT ... SELECT。这就是为什么你有 10 行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-13
  • 2012-02-13
  • 2019-10-05
  • 1970-01-01
  • 1970-01-01
  • 2014-03-15
  • 1970-01-01
相关资源
最近更新 更多