如何在引用自身的postgresql表中从给定ID递归获取所有ID?

标题可能不太清楚,所以让我们考虑这个示例(这不是我的代码,仅以这个示例为我的请求建模)

我有一个引用自己的表(如文件系统)

 id |  parent  | name
----+----------+-------
 1  |   null   |   /
 2  |    1     |  home
 3  |    2     |  user
 4  |    3     |  bin
 5  |    1     |  usr
 6  |    5     |  local

是否可以发出sql请求,所以如果我选择:

1我将得到一个包含2,3,4,5,6的表(因为这是根),因此匹配:

> /首页
> / home /用户
> / home / user / bin
> / usr
>等…

2我将得到一个包含3,4的表,这样匹配:

> / home /用户
> / home / user / bin

等等

最佳答案
使用recursive common table expression.始终从根开始,使用ID数组来获取WHERE子句中给定ID的路径.

对于ID = 1:

with recursive cte(id, parent, name, ids) as (
    select id, parent, name, array[id]
    from my_table
    where parent is null
union all
    select t.id, t.parent, concat(c.name, t.name, '/'), ids || t.id
    from cte c
    join my_table t on c.id = t.parent
)
select id, name 
from cte
where 1 = any(ids) and id <> 1

 id |         name          
----+-----------------------
  2 | /home/
  5 | /usr/
  6 | /usr/local/
  3 | /home/user/
  4 | /home/user/bin/
(5 rows)

对于ID = 2:

with recursive cte(id, parent, name, ids) as (
    select id, parent, name, array[id]
    from my_table
    where parent is null
union all
    select t.id, t.parent, concat(c.name, t.name, '/'), ids || t.id
    from cte c
    join my_table t on c.id = t.parent
)
select id, name 
from cte
where 2 = any(ids) and id <> 2

 id |         name          
----+-----------------------
  3 | /home/user/
  4 | /home/user/bin/
(2 rows)    

双向查询

这个问题真的很有趣.上面的查询效果很好,但是效率低下,因为它解析所有树节点,即使我们在请求叶子时也是如此.更为强大的解决方案是双向递归查询.内部查询从给定的节点到顶部,而外部查询从该节点到底部.

with recursive outer_query(id, parent, name) as (
    with recursive inner_query(qid, id, parent, name) as (
        select id, id, parent, name
        from my_table
        where id = 2        -- parameter
    union all
        select qid, t.id, t.parent, concat(t.name, '/', q.name)
        from inner_query q
        join my_table t on q.parent = t.id
    )
    select qid, null::int, right(name, -1)
    from inner_query
    where parent is null
union all
    select t.id, t.parent, concat(q.name, '/', t.name)
    from outer_query q
    join my_table t on q.id = t.parent
)
select id, name
from outer_query
where id <> 2;          -- parameter

转载注明原文:如何在引用自身的postgresql表中从给定ID递归获取所有ID? - 代码日志