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

Postgres中如何将邻接表层级数据转为任意深度嵌套JSON树

PostgreSQL 生成邻接表树形JSON结构方案

你遇到的报错是PostgreSQL对递归CTE的语法限制:递归查询的递归项中不允许使用聚合函数,你的自底向上聚合子节点的思路直接用递归CTE写会触发这个限制,需求完全可以实现,推荐两种可行方案:


方案1:使用PL/pgSQL递归函数(最简洁,支持任意深度)

这个方案逻辑清晰,无需处理递归CTE的语法限制,生成的结构完全符合要求:

-- 创建递归函数
CREATE OR REPLACE FUNCTION get_creature_tree(p_parent text) 
RETURNS json AS $$
BEGIN
    RETURN (
        SELECT json_agg(
            json_build_object(
                'name', name,
                'children', COALESCE(get_creature_tree(name), '[]'::json)
            )
        )
        FROM creatures
        WHERE parent IS NOT DISTINCT FROM p_parent
    );
END;
$$ LANGUAGE plpgsql STABLE;

-- 调用函数获取根节点树形结构
SELECT get_creature_tree(NULL) AS creature_tree;

执行后返回的JSON和你要求的格式完全一致。


方案2:纯SQL递归CTE实现(无需自定义函数)

如果不希望创建自定义函数,可以通过分层聚合的方式绕过递归项不能用聚合的限制,先递归获取所有节点的深度,再从最底层逐层向上聚合:

WITH RECURSIVE node_depth AS (
    -- 根节点深度为1
    SELECT name, parent, 1 AS depth
    FROM creatures
    WHERE parent IS NULL
    
    UNION ALL
    
    SELECT c.name, c.parent, nd.depth + 1 AS depth
    FROM creatures c
    JOIN node_depth nd ON c.parent = nd.name
),
max_depth AS (
    SELECT MAX(depth) AS max FROM node_depth
),
layered_agg AS (
    -- 从最深层开始,逐层向上聚合
    SELECT 
        nd.parent,
        json_build_object('name', nd.name, 'children', '[]'::json) AS node
    FROM node_depth nd
    JOIN max_depth md ON nd.depth = md.max
    
    UNION ALL
    
    SELECT 
        nd.parent,
        json_build_object(
            'name', nd.name,
            'children', COALESCE(json_agg(la.node) FILTER (WHERE la.node IS NOT NULL), '[]'::json)
        ) AS node
    FROM node_depth nd
    LEFT JOIN layered_agg la ON la.parent = nd.name
    GROUP BY nd.name, nd.parent
)
SELECT json_agg(node) AS creature_tree
FROM layered_agg
WHERE parent IS NULL;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 04:27:02