2016-07-15 87 views
1

这是一个表的结构:Id Name ParentIdSQL:删除所有嵌套自参照记录

ParentId是一个外键,主列Id。现在让我们说我有这样几行:(只显示ParentId来自行)

 01 
/ \ 
    5  2 
     /\ 
     3 4 

我已经安装InsteadOfDelete触发,如下图所示:

CREATE TRIGGER [dbo].[tr_tb] ON [dbo].[Some_tb] 
    INSTEAD OF DELETE 
AS 
    /*Delete from another table*/ 
    DELETE FROM SomeOther_tb WHERE OtherId IN(SELECT Id FROM deleted); 

    /*Delete childs from this table*/ 
    DELETE FROM Some_tb WHERE ParentId IN(SELECT Id FROM deleted); 

    /*Delete from this table*/ 
    DELETE FROM Some_tb WHERE Id IN(SELECT Id FROM deleted); 

Id = 01(记录查询以上孩子的代表性)被删除。只有直接孩子(Id=5,2)被删除,留下子孩子记录(Id = 3,4)。

我假设InsteadOfTrigger功能不会触发子女(Id=5,2)。

如何解决此问题,以便删除所有嵌套的子节点?

+1

为什么不利用级联删除,因为这似乎正是你在做什么? – UnhandledExcepSean

+0

不能因为多个级联路径:https://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/ –

+0

所以这是一个自动加入表格? – UnhandledExcepSean

回答

0
DECLARE @DELETED TABLE(
     Id BIGINT 
    ); 


WITH LoopCTE AS (
    SELECT Id, ParentId,0 AS steps 
    FROM dbo.Some_tb 
    WHERE Id IN (SELECT Id FROM deleted) 

    UNION ALL 

    SELECT mgr.Id, mgr.ParentId, parent.steps +1 AS steps 
    FROM LoopCTE AS parent 
    INNER JOIN dbo.Some_tb AS mgr 
     ON parent.Id = mgr.ParentId 
) 
INSERT INTO @DELETED SELECT Id FROM LoopCTE AS u; 

/*Delete from another table*/ 
DELETE FROM SomeOther_tb WHERE OtherId IN(SELECT Id FROM @DELETED); 


/*Delete childs from this table*/ <-- No longer required 
/*DELETE FROM Some_tb WHERE ParentId IN(SELECT Id FROM @DELETED);*/ 

/*Delete from this table*/ 
DELETE FROM Some_tb WHERE Id IN(SELECT Id FROM @DELETED); 
+0

“@删除”表中没有必要使用 –

+0

@AlexKudryashev然后如何插入已被CTE发现的新'Ids'?联盟与删除? –

+0

您不需要**插入**。直接使用CTE来'删除'你想要的东西。 –

1
CREATE TRIGGER [dbo].[tr_tb] ON [dbo].[Some_tb] 
    INSTEAD OF DELETE 
AS 
    DECLARE @DELETED TABLE(
     ID BIGINT 
    ) 

    INSERT INTO @DELETED 
     SELECT Id FROM deleted 

    --could use a CTE here as well for better performance 
    WHILE @@ROWCOUNT>0 
     BEGIN 
      INSERT INTO @DELETED 
       SELECT Id 
       FROM Some_tb t 
       inner join @DELETED d on d.parentid=t.id 
     END 

    /*Delete from another table*/ 
    DELETE FROM SomeOther_tb WHERE OtherId IN(SELECT Id FROM @DELETED); 

    /*Delete childs from this table*/ 
    DELETE FROM Some_tb WHERE ParentId IN(
     SELECT Id FROM @DELETED WHERE ID NOT IN (SELECT ID FROM DELETED) 
    ); 

    /*Delete from this table*/ 
    DELETE FROM Some_tb WHERE Id IN(SELECT Id FROM DELETED); 
+0

非常感谢您的答案。我确实尝试了一些CTE的东西,并写下了我自己的答案。非常感谢您的意见和建议。 –

1

MS SQL Server 2008支持通用表表达式(CTE),这是伟大的分层数据。 CTE也可能存在触发器。

CREATE TRIGGER [dbo].[tr_tb] ON [dbo].[Some_tb] 
    INSTEAD OF DELETE 
AS 
    /*Delete from another table*/ 
    ;with tbl as (--this is CTE 
     --anchor query (top level) 
     select t.id, t.parentid 
     from someOther_tb t 
     inner join deleted d on d.id = t.parentid 
     union all 
     --recursive query 
     select t.id, t.parentid 
     from someOther_tb t 
     inner join tbl on tbl.id=t.parentid 
    ) 
    delete someOther_tb 
    where id in (select id from tbl)   

    --Now it is safe to delete from main table 
    DELETE FROM Some_tb WHERE Id IN(SELECT Id FROM deleted) 
+0

谢谢亚历克斯,我猜'从someOther_tb'不正确,因为'ParentId'和'Id'来自同一张表。顺便说一句,我添加了CTE实现的答案。请让我知道你对此的看法。 –

+0

你的意思是'some_tb'和'someOther_tb'(如OP)是同一张表吗? –

+0

此处的目的是删除'Some_tb'的所有嵌套子元素,包括'SomeOther_tb'中用'Some_tb'引用的记录。 问题所描述的问题是所有嵌套的孩子没有被删除。 –