如何在 PostgreSQL 中以相反的顺序获取完整的树节点路径?

How to get full tree node path in reverse order in PostgreSQL?

我有以下table

CREATE TABLE descriptor_value (
    id bigint NOT NULL,
    value varchar(250),
    parent_id bigint
) ;

id列是主键。

我有三行

id | value | parent_id
----------------------
1  | foo   | null
2  | bar   | 1
3  | baz   | 2

而且我需要创建视图以相反的顺序显示 every 节点的完整路径。例如,如果我们只考虑 baz,则必须有以下输出:

child_id | parent_id | level
----------------------------
 3       | 3         | 1
 3       | 2         | 2    
 3       | 1         | 3

谁能告诉我如何在 PgSQL 12 中做到这一点?

使用递归 CTE:

with recursive cte as (
      select dv.id, dv.value, dv.parent_id, dv.parent_id as root, 1 as lev,
             dv.id::text as path
      from descriptor_value dv
      where dv.parent_id is null
      union all
      select dv.id, dv.value, dv.parent_id, cte.root, lev + 1,
             cte.path || '-->' || (dv.id::text)
      from cte join
           descriptor_value dv
           on dv.parent_id = cte.id
    )
select cte.id, cte.value, cte.parent_id, cte.root, lev,
       max(lev) over (partition by cte.root) - lev + 1 as level
from cte
order by root, lev;

Here 是一个 db<>fiddle.

我认为您想从叶子遍历树到节点。那将是:

with recursive cte as (
    select id child_id, id parent_id, 1 lvl, parent_id real_parent_id
    from descriptor_value dv
    where not exists (select 1 from descriptor_value dv1 where dv1.parent_id = dv.id)
    union all
    select c.child_id, dv.id parent_id, lvl + 1, dv.parent_id
    from cte c
    inner join descriptor_value dv on dv.id = c.real_parent_id
)
select child_id, parent_id, lvl from cte order by lvl

递归查询的锚点从不是任何其他节点父节点的行开始。然后我们爬上树,每一步递增 lvl,直到到达根。

Demo on DB Fiddle:

child_id | parent_id | lvl
-------: | --------: | --:
       3 |         3 |   1
       3 |         2 |   2
       3 |         1 |   3

如果想从任意一个节点开始,那就更简单了:我们只需要改变anchor的定义即可:

with recursive cte as (
    select id child_id, id parent_id, 1 lvl, parent_id real_parent_id
    from descriptor_value dv
    union all
    select c.child_id, dv.id parent_id, lvl + 1, dv.parent_id
    from cte c
    inner join descriptor_value dv on dv.id = c.real_parent_id
)
select child_id, parent_id, lvl from cte order by child_id, lvl

Yields:

child_id | parent_id | lvl
-------: | --------: | --:
       1 |         1 |   1
       2 |         2 |   1
       2 |         1 |   2
       3 |         3 |   1
       3 |         2 |   2
       3 |         1 |   3