06. 父子节点(树)遍历写法小结

对于树/图的遍历,通常有2种算法来实现:迭代(Iteration)和递归(Recursion),迭代是利用循环反复取值/赋值的过程;递归则是反复自己调用自己来获得最终结果。
SQL Server里的递归有32层嵌套限制,目的在于防止代码进入死循环,除非使用提示OPTION (MAXRECURSION 0)。

测试数据:

if OBJECT_ID('city') is not null

    drop table city

GO

create table city

(

id    int,

name  nvarchar(10),

pid   int,

depth int

)

GO

insert into city 

select  1,N'江苏省',0,0 union all

select  2,N'南京市',1,1 union all

select  3,N'玄武区',2,2 union all

select  4,N'鼓楼区',2,2 union all

select  5,N'浙江省',0,0 union all

select  6,N'杭州市',5,1 union all

select  7,N'西湖区',6,2 union all

select  8,N'滨江区',6,2 union all

select  9,N'苏州市',1,1 union all

select 10,N'吴中区',9,2 union all

select 11,N'吴江区',9,2

一. 查找子节点
查找节点1的所有子节点,返回结果如下:

id name pid depth
1 江苏省 0 0
2 南京市 1 1
3 玄武区 2 2
4 鼓楼区 2 2
9 苏州市 1 1
10 吴中区 9 2
11 吴江区 9 2

1. 迭代
(1) 不借助depth,通过not in来向下查找

if OBJECT_ID('f_get_child') is not null

    drop function f_get_child

GO

create function f_get_child

(

@id int

)

returns @t table(id int)

as

begin

    insert into @t select @id

    --insert into @t select id from city where pid = @id

    while @@ROWCOUNT>0

    begin

        insert into @t 

        select a.id 

        from city a inner join @t b on a.pid = b.id

        where a.id not in(select id from @t)

    end

    return

end

GO

select * from city where id in(select id from f_get_child(1))

(2) 通过depth来逐层查找

if OBJECT_ID('f_get_child') is not null

    drop function f_get_child

GO

create function f_get_child

(

@id int

)

returns @t table(id int, depth int)

begin

    declare @depth int

    set @depth = 0

    insert @t select ID,@depth from city where ID =@ID

    while @@ROWCOUNT>0

    begin

        set @depth = @depth + 1

        insert @t select a.ID,@depth

          from city a, @t b

         where a.pid = b.ID

           and b.depth = @depth - 1

    end    

    return      

end

GO

select * from city where id in(select id from f_get_child(1))

2. 递归
(1) 自定义函数递归

if OBJECT_ID('f_get_child') is not null

    drop function f_get_child

GO

create function f_get_child

(

@id int

)

returns @t table(id int)

as

begin

    declare @pid int

    set @pid = null

    insert into @t

    select @id 

    union all 

    select id from city where pid = @id

    

    if exists(select 1

        from city a inner join @t b on a.pid = b.id

        where a.id not in(select id from @t))

    begin

        insert into @t 

        select a.id 

        from city a inner join @t b on a.pid = b.id

        where a.id not in(select id from @t)

        union all

        select * from f_get_child(@pid)

    end

    return

end

GO

select * from city where id in(select * from f_get_child(1))

(2) CTE递归

declare @id int

set @id = 1;

with tmp

as

(

select * from city where id = @id

union all

select a.* from city a

inner join tmp b

on a.pid = b.id

)

select * from tmp order by id

二. 查找父节点
查找节点8的所有父节点,返回结果如下:

id name pid depth
5 浙江省 0 0
6 杭州市 5 1
8 滨江区 6 2

1. 迭代
父节点只有一个,不需要做什么限制,一直往上级查找pid就可以了。

if OBJECT_ID('f_get_parent') is not null

    drop function f_get_parent

GO

create function f_get_parent

(

@id int

)

returns @t table(id int)

as

begin

    declare @pid int

    insert into @t select @id

    select @pid = pid from city where id = @id

    while @pid<>0

    begin

        insert into @t values(@pid)

        select @pid=pid from city where id=@pid

    end

    return

end

GO

select * from city where id in(select * from f_get_parent(8))

2. 递归
(1) 自定义函数递归

if OBJECT_ID('f_get_parent') is not null

    drop function f_get_parent

GO

create function f_get_parent(@id int)

returns @t table(id int)

AS

begin

    declare @pid int

    select top 1 @pid = pid

    from city

    where id = @id

    if @pid <> 0

    begin

        insert into @t

        select @id 

        union all

        select * from f_get_parent(@pid)

    end

    else

    begin

        insert into @t

        select @id 

    end

    return

end

GO

select * from city where id in(select * from f_get_parent(8))

(2) CTE递归

declare @id int

set @id = 8;

with tmp

as

(

select * from city where id = @id

union all

select a.* from city a

inner join tmp b

on a.id = b.pid

)

select * from tmp order by id

 

你可能感兴趣的:(遍历)