• 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

    注意:(更新:09/28/2018)

    之前通过递归函数写的父/子节点遍历逻辑有问题,只能遍历2层深度的节点,函数递归可参考以下链接:

    Recursion in T–SQL

    https://technet.microsoft.com/en-us/library/aa175801(v=sql.80).aspx

    Recursive Scalar Function in T-SQL

    https://stevestedman.com/2013/04/recursive-scalar-function-in-t-sql/

  • 相关阅读:
    Js 循环 forEach, map, for, for...in, for...of
    es6 let const
    es6 Symbol类型
    es6 Reflect 与 Proxy
    es6 Map&Set
    es6箭头函数
    es6数组Arrary
    学写网站(一)前端配置之安装nvm、node、npm
    python获取当前执行代码所在文件的地址、主程序所在地址
    scrapy中的选择器下载中间件downloadmiddlewares
  • 原文地址:https://www.cnblogs.com/seusoftware/p/3269514.html
Copyright © 2020-2023  润新知