SQL之树形结构无限级联删除(转)


http://www.cnblogs.com/NewJourney/articles/2366243.html

摘要:树形结构的删除存在其自身特点,特别对于无限级联的树形结构更是如此,今天我们一块看一下如何处理无限级联树的删除问题。

主要内容:

  1. 初始工作
  2. SQL实现
  3. 总结

一、初始工作

为了更好的说明问题,我首先建立两张表:

 

  1. --Create Table  
  2. IF EXISTS (SELECT [name] FROM dbo.sysobjects WHERE [name]='Tree' AND type='u' )  
  3.     BEGIN   
  4.         IF EXISTS(SELECT [name] FROM dbo.sysobjects WHERE [name]='Info' AND type='u')  
  5.             DROP TABLE Info  
  6.         DROP TABLE Tree  
  7.     END  
  8. ELSE  
  9.     BEGIN  
  10.         CREATE TABLE Tree   
  11.         (  
  12.             id BIGINT PRIMARY KEY,  
  13.             [name] NVARCHAR(50) NOT NULL,  
  14.             parentID BIGINT FOREIGN KEY REFERENCES Tree(id) ON DELETE NO ACTION NOT NULL  
  15.         )  
  16.     END  
  17. IF EXISTS(SELECT [name] FROM dbo.sysobjects WHERE [name]='Info' AND type='u')  
  18.     DROP TABLE Info  
  19. ELSE  
  20.     BEGIN  
  21.         CREATE TABLE Info  
  22.         (  
  23.             id BIGINT PRIMARY KEY FOREIGN KEY REFERENCES Tree(id) ON DELETE CASCADE,  
  24.             info NVARCHAR(500)  
  25.         )  
  26.     END   

 

这里我们建立了两张表:"Tree"和"Info"。"Tree"作为我们的树形结构信息存放表,里面包含节点编号、节点名称和父类编号;"Info"表中存放每个节点的各种信息(当然可以有多张"Info"表,这里简单起见只有一个信息表)。

到了这里可能会有朋友说:在创建表的时候直接在"parentID"后面加上"DELETE CASCADE"问题不就解决了吗?由于"Tree"表是自身关联的,这样一来删除其父类的话就会将子类删除?何必弄的那么麻烦呢?如果真的这样的话我想再好不过了,今天的话题也就简单了。事实上那样是不可行的,SQL Server会抛出如下错误告诉你那样做是不可以的(其实这也是自身关联的特点:不能设定"DELETE CASCADE",当然对于Info表式没有问题的):

消息 1785 ,级别 16 ,状态 0 ,第 1 行

将 FOREIGN KEY 约束 'FK__Tree__parentID__07F6335A' 引入表 'Tree' 可能会导致循环或多重级联路径。请指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION ,或修改其他 FOREIGN KEY 约束。

消息 1750 ,级别 16 ,状态 0 ,第 1 行

无法创建约束。请参阅前面的错误消息。

接着我们插入一些测试数据(姑且认为顶级节点父类为其自身):

 

  1. -- Insert Data  
  2. DELETE FROM dbo.Tree  
  3. DELETE FROM dbo.Info  
  4. INSERT INTO dbo.Tree VALUES(1,'A',1)  
  5. INSERT INTO dbo.Tree VALUES(2,'B',1)  
  6. INSERT INTO dbo.Tree VALUES(3,'C',1)  
  7. INSERT INTO dbo.Tree VALUES(4,'D',2)  
  8. INSERT INTO dbo.Tree VALUES(5,'E',2)  
  9. INSERT INTO dbo.Tree VALUES(6,'F',3)  
  10. INSERT INTO dbo.Tree VALUES(7,'G',3)  
  11. INSERT INTO dbo.Tree VALUES(8,'H',4)  
  12. INSERT INTO dbo.Tree VALUES(9,'I',4)  
  13. INSERT INTO dbo.Tree VALUES(10,'J',4)  
  14. INSERT INTO info VALUES(1,'AA')  
  15. INSERT INTO info VALUES(2,'BB')  
  16. INSERT INTO info VALUES(3,'CC')  
  17. INSERT INTO info VALUES(4,'DD')  
  18. INSERT INTO info VALUES(5,'EE')  
  19. INSERT INTO info VALUES(6,'FF')  
  20. INSERT INTO info VALUES(7,'GG')  
  21. INSERT INTO info VALUES(8,'HH')  
  22. INSERT INTO info VALUES(9,'II')  
  23. INSERT INTO info VALUES(10,'JJ')  

 

二、SQL实现

有了表和数据我们就开始思考如何解决级联删除的问题吧。既然是无限级联,也就是说根本不知道深度,当然最简单的方法就是使用递归或者通过循环来实现。姑且不论这种方法如何实现,关键是这种方法删除的时候只会从上往下删除(也就是从父节点到子节点的顺序),而由于外键约束的关系我们这样删除是不可行的。因此,我们必须找到一种能够从最底端的子节点依次往上删除的方法。下面我们直接看一下SQL:

 

  1. SET ANSI_NULLS ON  
  2. GO  
  3. SET QUOTED_IDENTIFIER ON  
  4. GO  
  5. -- =============================================  
  6. -- Author:      KenshinCui  
  7. -- Create date: 2010.11.22  
  8. -- Description: 无限级联删除  
  9. -- =============================================  
  10. CREATE PROCEDURE NodeDelete  
  11.     @id BIGINT  
  12. AS  
  13. BEGIN  
  14.     DECLARE @tbIds TABLE(id BIGINT)  
  15.     DECLARE @tempTbs TABLE(id BIGINT)  
  16.     DECLARE @tb TABLE(id BIGINT,orderIndex BIGINT IDENTITY(1,1))  
  17.     INSERT INTO @tbIds(id) VALUES(@id)  
  18.     INSERT INTO @tempTbs(id) VALUES(@id)  
  19.     INSERT INTO @tb(id) VALUES(@id)  
  20.     WHILE EXISTS(SELECT id FROM @tbIds)  
  21.         BEGIN  
  22.             DELETE FROM @tbIds  
  23.             INSERT INTO @tb SELECT ID FROM dbo.Tree WHERE ParentID IN (SELECT ID FROM @tempTbs)  
  24.             INSERT INTO @tbIds SELECT ID FROM dbo.Tree WHERE ParentID IN (SELECT ID FROM @tempTbs)  
  25.             DELETE FROM @tempTbs  
  26.             INSERT INTO @tempTbs SELECT id FROM @tbIds  
  27.         END  
  28.     DECLARE @tid INT   
  29.     DECLARE myCursor CURSOR FOR SELECT id FROM @tb ORDER BY orderIndex DESC   
  30.     OPEN myCursor   
  31.     FETCH NEXT FROM myCursor INTO @tid   
  32.     WHILE @@fetch_status=0   
  33.     BEGIN  
  34.         DELETE FROM dbo.Tree WHERE ID =@tid  
  35.         FETCH NEXT FROM myCursor INTO @tid    
  36.     END  
  37.     CLOSE myCursor     
  38.     DEALLOCATE myCursor  
  39. END  
  40. GO  

 

这种方法的思路就是通过从上到下的顺序依次查找,首先将查找的内容放到一个Table类型的变量中,而这个变量本身就有一个排序字段可以排序。这样一来我们通过第一次遍历就可以将所要删除的节点id有序的存储到变量中,接着我们再通过倒序遍历的方式遍历这个变量执行删除。

三、总结

这问题关键注意以下几点:第一通过父类节点找子类节点时不一定只有一个子节点,找到的是一个集合,我们要通过一种类型来存放集合变量(也就是上面的table类型);第二就是如何将table变量作为堆栈使用(也就是顺序要可控的);第三是在第一循环之后得到的table型变量无法直接通过"delete from Tree where id in(select id from @tb order by desc)"的方式删除,因为子句中排序是有约束的(不是任何时候都可以的),这里我们是通过游标来解决的(当然还有别的方式)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值