我有以下表格:回复和消息。我想模仿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表填充了第一个触发器的数据(激发了第二个触发器)。但我真的不确定,我会感激一些帮助。谢谢!
答案 0 :(得分:1)
这样的事情对你有用吗?通过在Messages上使用单个触发器来删除递归,它会预先计算所有相关消息,因此您最多应该获得一个嵌套调用?它会删除所有相关消息并回复
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
答案 1 :(得分: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')
答案 2 :(得分:0)
我建议在触发器中一次生成所有删除,如Fergus Bown建议的那样,或者将删除逻辑移动到存储过程调用。在我们的应用程序中,我们对所有CRUD操作(创建,读取,更新,删除)使用存储过程方法。
缺点是新手支持DBA如果使用SQL删除单个Reply而不删除与之关联的所有其他消息,则可能会失败。但是这样的DBA应该知道使用存储过程(或者首先获得SQL权限)。