0

次のテーブルがあります: 返信とメッセージ。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

メッセージが削除されたときに返信の関連行を削除する最初のトリガーを作成しました。返信の行が削除されるたびに関連するメッセージを削除する別のトリガーを作成したいと思います。現在、返信は意味をなさない独自のメッセージになっています。2 番目のトリガーは次のとおりです。

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 テーブルが最初のトリガー (2 番目のトリガー) からのデータで満たされているためだと思います。しかし、本当にわからないので、助けていただければ幸いです。ありがとう!

4

3 に答える 3

1

このようなものはあなたのために働くでしょうか?メッセージで単一のトリガーを使用するだけで再帰を削除します。これにより、関連するすべてのメッセージが事前に計算されるため、ネストされた呼び出しは最大で 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
于 2012-12-10T13:59:53.643 に答える
0

助けてくれてありがとう、ファーガス、ありがとう。ただし、@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')
于 2012-12-10T14:12:22.170 に答える
0

Fergus Bown が提案したように、トリガーですべての削除を一度に生成するか、削除ロジックをストアド プロシージャ コールに移動することをお勧めします。このアプリケーションでは、すべての CRUD 操作 (作成、読み取り、更新、削除) にストアド プロシージャ アプローチを使用します。

欠点は、関連する他のすべてのメッセージを削除せずに SQL を使用して単一の返信を削除すると、初心者のサポート DBA が失敗する可能性があることです。しかし、そのような DBA は、代わりにストアド プロシージャを使用する (または最初に SQL を正しく取得する) ことを知っておく必要があります。

于 2012-12-10T14:16:43.583 に答える