【问题标题】:Nested triggers in SQLSQL中的嵌套触发器
【发布时间】:2012-12-10 13:08:30
【问题描述】:

我有以下表格:回复和消息。我想模仿 Facebook 的行为:当一条消息被删除时,所有相关的回复也会被删除。我的表格如下所示:

REPLIES
messageId                               replyId
6b61d107-dff3-4374-a3a2-75ac7478a2f2    865c873d-0210-482a-b8bd-371c4f07f0cf

MESSAGES
id                                      body
865c873d-0210-482a-b8bd-371c4f07f0cf    this is the reply
6b61d107-dff3-4374-a3a2-75ac7478a2f2    this is the message

我创建了一个第一个触发器,它可以在删除消息时删除回复中的相关行。我现在想创建另一个触发器,它会在每次删除回复中的一行时删除相关消息。现在回复变成了他们自己的消息,这是没有意义的。这是第二个触发器:

CREATE TRIGGER TRG_DEL_MESSAGES
ON Replies
FOR DELETE
AS
DELETE FROM Messages WHERE id = (SELECT replyId FROM DELETED)

当我尝试删除某些内容时会带来以下错误:

Maximum stored procedure, function, trigger, or view nesting level exceeded (limit 32).

显然存在一个无限循环,我认为这是因为 DELETED 表被第一个触发器(引发第二个触发器)的数据填充。但我真的不确定,我会很感激一些帮助。谢谢!

【问题讨论】:

  • Visual Studio 2010 中的 SQL Express。
  • 简单的答案是不要使用嵌套触发器......它们会导致您正在查看的问题。如果您改用存储过程,您的生活会更轻松,代码也不会那么混乱,因为它都在一个地方。
  • 另一个建议...你为什么不使用级联删除?
  • @Boomer:级联删除是我的第一次尝试,但我遇到了同样的问题。
  • 您在尝试级联删除之前是否禁用了触发器?

标签: asp.net sql sql-server triggers


【解决方案1】:

这样的东西对你有用吗?只需在消息上使用单个触发器即可删除递归,该触发器会预先计算所有相关消息,因此您应该最多获得一个嵌套调用?它会从消息和回复中删除所有相关消息

CREATE TRIGGER TRG_DEL_REPLIES
ON [Messages]
FOR DELETE
AS
BEGIN
    DECLARE @Related TABLE (MessageId uniqueidentifier)

    --get all related messages so that we don't recurse
    BEGIN
        WITH AllReplies (MessageId)
        AS
        (
            --Anchor
            SELECT  D.MessageId
            FROM    Deleted D
            UNION ALL
            --Recurse
            SELECT  R.ReplyId
            FROM    AllReplies AR
            JOIN    Replies R
                    ON AR.MessageId = R.MessageId
        )
        INSERT  INTO @Related
        SELECT  * 
        FROM    AllReplies
    END

    --delete the replies

    DELETE  R
    FROM    Replies R
    JOIN    @Related REL
            ON R.MessageId = REL.MessageId

    --delete the messages

    DELETE  M
    FROM    [Messages] M
    JOIN    @Related REL
            ON  REL.MessageId = M.MessageId 
    LEFT
    JOIN    DELETED D
            ON REL.MessageId = D.MessageId
    WHERE   D.MessageId IS NULL
END

要将其转换为您想要的存储过程,我会这样做,而不是执行多个单独删除的循环:

CREATE PROCEDURE DeleteMessageWithReplies(@messageId uniqueidentifier)
AS
BEGIN
    DECLARE @Related TABLE (MessageId uniqueidentifier)

    --get all related messages
    BEGIN
        WITH AllReplies (MessageId)
        AS
        (
            --Anchor
            SELECT  @messageId
            UNION ALL
            --Recurse
            SELECT  R.ReplyId
            FROM    AllReplies AR
            JOIN    Replies R
                    ON AR.MessageId = R.MessageId
        )
        INSERT  INTO @Related
        SELECT  * 
        FROM    AllReplies
    END

    --delete the replies

    DELETE  R
    FROM    Replies R
    JOIN    @Related REL
            ON R.MessageId = REL.MessageId

    --delete the messages that haven't already been deleted

    DELETE  M
    FROM    [Messages] M
    JOIN    @Related REL
            ON  REL.MessageId = M.MessageId 
END

【讨论】:

    【解决方案2】:

    感谢您的帮助弗格斯,我很感激。但是,正如@Ben 指出的那样,存储过程更容易编码。这是我刚刚写的,它可能会有所改进,但至少它有效。

    EXEC('CREATE PROCEDURE deleteMessageWithReplies(@messageId uniqueidentifier)
    AS
    BEGIN
        DECLARE @repliesCount int
        SELECT @repliesCount = (SELECT COUNT(*) FROM Replies WHERE messageId=@messageId)
        DECLARE @cpt int
    SET @cpt = 0
    DELETE FROM Messages WHERE id = @messageId
    WHILE @cpt < @repliesCount
    BEGIN
        DECLARE @replyId uniqueidentifier
        SELECT @replyId = (SELECT TOP 1 replyId FROM Replies WHERE messageId=@messageId)
        DELETE FROM Replies WHERE replyId = @replyId
        DELETE FROM Messages WHERE id=@replyId
        SET @cpt = @cpt+1
    END
    END')
    

    【讨论】:

      【解决方案3】:

      我建议要么按照 Fergus Bown 的建议在触发器中一次生成所有删除,要么将删除逻辑移至存储过程调用。在我们的应用程序中,我们对所有 CRUD 操作(创建、读取、更新、删除)使用存储过程方法。

      缺点是新手支持 DBA 如果他们使用 SQL 删除单个回复而不删除与之关联的所有其他消息,他们可能会犯错。但是这样的 DBA 应该知道改用存储过程(或者首先获得正确的 SQL)。

      【讨论】:

        猜你喜欢
        • 2023-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-08
        • 2014-06-07
        • 2023-04-07
        • 2015-10-19
        • 1970-01-01
        相关资源
        最近更新 更多