通过复杂的层次结构递归

时间:2019-12-29 12:59:47

标签: sql postgresql recursion

我试图将递归用于看起来像这样的非常复杂的层次结构:

Root    
|    
Second   
|   
Third   
|   
Leaf   

但是也可以像这样(没有第二个):

Root       
|   
Third   
|   
Leaf

我的SQL查询如下:

with recursive relations as 
(select parent_id, child_id, 1 as h_level, array[parent_id,child_id] as fullpath
 from public.entity_relations where parent_type = 4
union all 
 select c.parent_id, c.child_id, p.h_level+1, p.fullpath || c.child_id 
 from public.entity_relations c join relations p on p.child_id = c.parent_id)
select * from relations;

以下是带有关系表和递归查询的sqlfiddle链接:
http://sqlfiddle.com/#!17/31793/1/0

问题在于递归返回此层次结构的子路径。
我只对每个层次结构的最完整路径感兴趣,在此示例中,仅对以下记录感兴趣:

BB  CC  2   A,BB,CC
C   D   3   A,B,C,D

注意:我知道递归需要“子路径”, 所以我正在寻找一种在递归后过滤掉多余记录的方法。

2 个答案:

答案 0 :(得分:2)

在最终查询中,您只能选择叶子(没有孩子的节点):

with recursive relations as (
    select 
        parent_id, 
        child_id, 
        1 as h_level, 
        array[parent_id,child_id] as fullpath
    from entity_relations where parent_type = 4
union all 
    select 
        c.parent_id, 
        c.child_id, 
        p.h_level+1, 
        p.fullpath || c.child_id 
    from entity_relations c 
    join relations p on p.child_id = c.parent_id
)
select *
from relations r
where not exists (
    select from entity_relations e
    where e.parent_id = r.child_id
)

 parent_id | child_id | h_level | fullpath
-----------+----------+---------+-----------
 C         | D        |       2 | {A,C,D}
 BB        | CC       |       2 | {A,BB,CC}
 C         | D        |       3 | {A,B,C,D}
(3 rows)

事实证明,有两条路径通向叶子D。您可以根据级别选择其中一种。

...
select distinct on (child_id) *
from relations r
where not exists (
    select from entity_relations e
    where e.parent_id = r.child_id
)
order by child_id, h_level desc

 parent_id | child_id | h_level | fullpath
-----------+----------+---------+-----------
 BB        | CC       |       2 | {A,BB,CC}
 C         | D        |       3 | {A,B,C,D}
(2 rows)    

答案 1 :(得分:1)

如果我理解正确,那么您想要:

with recursive relations as (
      select parent_id, child_id, 1 as h_level, array[parent_id,child_id] as fullpath
      from public.entity_relations
      where parent_type = 4
      union all 
      select c.parent_id, c.child_id, p.h_level+1, p.fullpath || c.child_id 
      from public.entity_relations c join
           relations p
           on p.child_id = c.parent_id
     )
select r.*
from relations r
where not exists (select 1
                  from relations r2
                  where r2.fullpath @> r.fullpath and
                        r2.fullpath <> r.fullpath
                 );