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

标签 postgresql hierarchical-data recursive-query

标题可能不是很清楚所以让我们考虑这个例子(这不是我的代码,只是拿这个例子来模拟我的请求)

我有一个引用自身的表(就像一个文件系统)

 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/user/bin
  • /用户
  • 等...

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

  • /家/用户
  • /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 - 如何在引用自身的 postgresql 表中递归地获取从给定 ID 开始的所有 ID?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44874366/

相关文章:

node.js - 关系中的 TypeORM OneToMany 过滤器不影响结果

postgresql - "user"PostgreSQL 附近的语法错误,即使引用了用户

sql-server - X 次递归循环后停止查询

postgresql - 删除递归 child

postgresql - 在 PostgreSQL 中创建间隔的不同方法

ruby-on-rails - PG::错误:错误:关系 "users"在 Heroku 上不存在

python - Django 休息框架 : Derived model serializer fields

sql-server - 什么工具可以从 SQL Server 分层数据生成图表?

MYSQL:为每一行构建一个字符串的递归过程

sql - Oracle:分层查询中的编号组