You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL递归CTE中如何用UNION ALL实现UNION去重并保留层级路径

出现重复数据的核心原因是你的表中存在多条指向同一子节点的路径(比如节点7既可以通过1→2→3→4→5→6→7的长路径到达,也可以通过1→7的短路径到达),UNION ALL不会自动去重,所以会生成多条对应同一child的记录。另外递归CTE的递归部分不允许子查询引用CTE自身,所以你之前写的EXISTS条件会触发语法错误。

你可以先通过递归CTE生成所有可能的遍历路径,再在外层对child字段去重即可,这里提供两种通用实现:


方案1:PostgreSQL专用(DISTINCT ON语法更简洁)

with recursive cte as (
    select
        parent,
        child,
        0 as level,
        array[parent, child] as path
    from stack
    where parent = 1
    union all
    select
        s.parent,
        s.child,
        c.level + 1,
        c.path || s.child
    from cte c
    inner join stack s on c.child = s.parent
    where c.child is not null
        -- 可选加该条件避免数据存在循环时死循环
        and s.child != all(c.path)
)
-- DISTINCT ON 按child分组,每组取排序后的第一条
select distinct on (child) parent, child, level, path
from cte
-- 按level升序取每个节点的最短路径,如需最长路径改为降序即可
order by child, level asc;

方案2:全数据库通用(窗口函数实现)

with recursive cte as (
    select
        parent,
        child,
        0 as level,
        array[parent, child] as path
    from stack
    where parent = 1
    union all
    select
        s.parent,
        s.child,
        c.level + 1,
        c.path || s.child
    from cte c
    inner join stack s on c.child = s.parent
    where c.child is not null
        and s.child != all(c.path)
),
ranked_cte as (
    select 
        *,
        row_number() over(partition by child order by level asc) as rn
    from cte
)
select parent, child, level, path
from ranked_cte
where rn = 1;

执行上述语句后,返回的child列表和你之前用UNION得到的去重结果完全一致,同时会携带对应节点的深度和遍历路径。


内容的提问来源于stack exchange,提问作者zemirco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 09:54:03