在单个SQL查询中返回json层次结构

在单个SQL查询中返回json层次结构,sql,postgresql,recursion,hierarchical-data,Sql,Postgresql,Recursion,Hierarchical Data,我需要创建一个SQL查询(postgres 9.5.3),它返回一个分层json结果。这是我到目前为止写的代码 WITH RECURSIVE q AS ( WITH c AS ( SELECT pc."Id", pc."Description" FROM "ProductCategories" pc WHERE pc."Active" = true ) SELECT pc, ARRAY[c] as "Children"

我需要创建一个SQL查询(postgres 9.5.3),它返回一个分层json结果。这是我到目前为止写的代码

WITH RECURSIVE q AS ( 
    WITH c AS (
        SELECT pc."Id", pc."Description"
        FROM "ProductCategories" pc 
        WHERE pc."Active" = true 
    )
    SELECT pc, ARRAY[c] as "Children", ARRAY[pc."Id"] as "Path" 
    FROM "ProductCategories" pc 
    LEFT JOIN c ON pc."Id" = c."Id"
    WHERE NULLIF(pc."ParentId", 0) IS NULL 
    AND pc."Active" = true 
    UNION ALL 
    SELECT pc_descendant, array_append(q."Children", c), q."Path" || pc_descendant."Id" 
    FROM q 
    JOIN "ProductCategories" pc_descendant ON pc_descendant."ParentId" = (q.pc)."Id" 
    LEFT JOIN c ON pc_descendant."Id" = c."Id"
    WHERE pc_descendant."Active" = true 
)
SELECT * FROM q
我无法创建分层对象
子对象
。对于这些结构

A
    B
        C
D
    E
array\u append
函数似乎将任何子元素追加到单个数组中:

A.Children = [ {B}, {C}, {D} ]    //for category A
我需要结构:

  A.Children = [ {B, Children = [ {C, Children = [ {D} ] } ] } ]
如何更改查询以实现此目的?
关于

我不确定这是否可能,至少以简单方便的方式

然而,使用“真”递归似乎很简单

下面是一个简单的例子:

create temp table t(id int, parent int, name text) on commit drop;

insert into t values
  (1,null,'john'),
  (2,1,'jane'),
  (3,1,'jack'),
  (4,2,'julian');

create or replace function build_family(p_parent int) returns setof jsonb as $$

  select
    case 
      when count(x) > 0 then jsonb_build_object('name', t.name, 'family', jsonb_agg(f.x))
      else jsonb_build_object('name', t.name)
    end
  from t left join build_family(t.id) as f(x) on true
  where t.parent = p_parent or (p_parent is null and t.parent is null)
  group by t.id, t.name;

$$ language sql;


select jsonb_pretty(build_family) from build_family(null::int);
结果是

┌──────────────────────────────────────┐ │ jsonb_pretty │ ├──────────────────────────────────────┤ │ { ↵│ │ "name": "john", ↵│ │ "family": [ ↵│ │ { ↵│ │ "name": "jane", ↵│ │ "family": [ ↵│ │ { ↵│ │ "name": "julian"↵│ │ } ↵│ │ ] ↵│ │ }, ↵│ │ { ↵│ │ "name": "jack" ↵│ │ } ↵│ │ ] ↵│ │ } │ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ 杰森布│ ├──────────────────────────────────────┤ │ { ↵│ │ “姓名”:“约翰”,↵│ │ “家庭”:[↵│ │ { ↵│ │ “姓名”:“简”,↵│ │ “家庭”:[↵│ │ { ↵│ │ “姓名”:“朱利安”↵│ │ } ↵│ │ ] ↵│ │ }, ↵│ │ { ↵│ │ “姓名”:“杰克”↵│ │ } ↵│ │ ] ↵│ │ } │ └──────────────────────────────────────┘ 我希望您可以根据您的数据对其进行调整


祝你好运。

这真是太棒了-我喜欢这个优雅的解决方案,它真的展示了PostgreSQL的强大!我还将
'family',jsonb_agg(f.x)
更新为
'family',当jsonb_agg(f.x)!='[null]'::jsonb然后jsonb_agg(f.x)else'[]::jsonb end
返回“…
“family”:…
而不是“…
”family:[null]…
@bobmarksie这取决于IMO的要求。除您的建议外,我还更新了答案,以删除不必要的空“family”键。您好,在这种情况下,是否有人会在第一个和第二个递归循环中发生这种情况。是否可以在CTE中重新布线?(只是为了理解这个查询)。@JongzPuangput不,这是不可能的。函数使用表并自己递归调用。在一条语句中完成所有这些都是不可能的。抱歉,回答晚了。