SQL应用之查找根节点

MSSQL提供了CTE递归取数据的方法,但是没有直接提供一个给定任意节点查找其根节点的方法(也是ms sql 2008之后的版本有我不知道)。
此外,如果数据库提供的数据出现死循时,如果没有相应的检测机制,必然导致数据库服务器资源耗尽。因此查找根节点的SQL片断(或存储过程)也是相当有用的。

declare @table table(Id int,PId int,Name varchar(20))
insert into @table values(1,NULL,'A'),(2,8,'B'),(3,1,'B'),(4,2,'B'),(5,2,'B'),
    (6,3,'B'),(7,4,'B'),(8,7,'B'),(9,8,'B')

-- 循环中开始2 -> 8 -> 7 -> 4 -> 2
-- 循环外开始9 -> 8 -> 7 -> 4 -> 2 -> 8 ->... 

declare @paramId int = 7,@RootPId int = NULL,@Id int,@tmpId int,
    @count int = (select COUNT(*) from @table),@i int = 0
set @Id = @paramId
while 1=1
  begin
        set @i += 1
        select @tmpId = PId from @table where Id = @Id  -- 多值不合法,但只取一个值
        if (@RootPId is null and @tmpId is null) 
            OR (@RootPId is not null and @tmpId = @RootPId)
            OR @Id = @tmpId      /*自身引用死循环*/ 
            OR @tmpId = @paramId /*循环中死循环*/ 
            OR @i > @count       /*各种情况的死循环*/
            break
        set @Id = @tmpId
  end
select @Id

-- 警告: 聚合或其他 SET 操作消除了 Null 值。
-- (1)(select PId from @table where Id = @Id) Is not null
-- (2)set @Id int = (select Id from @table)
-- 运行时错误:子查询返回的值不止一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,或子查询用作表达式时,这种情况是不允许的。

注:以上sql目的在于查找根节点,防止死循环,对出现“循环数据”时返回结果未作严格处理,根据需要稍作修改即可。

你可能感兴趣的:(数据库,数据库,sql)