PostgreSQL:如何从 GROUP BY ROLLUP 数据生成 JSON

PostgreSQL: How to Generate JSON from GROUP BY ROLLUP data

我需要一些帮助来将 Group By ROLLUP(...) 聚合到嵌套的 JSON.

我的查询(结果)如下图


查询#1

SELECT main, sub, subsub, count(*) FROM test 
GROUP BY ROLLUP(main, sub, subsub)
ORDER BY main, sub, subsub;

结果

| main | sub | subsub | count |
| ---- | --- | ------ | ----- |
| c    | c-1 | c-1-1  | 1     |
| c    | c-1 | c-1-2  | 1     |
| c    | c-1 |        | 2     |
| c    |     |        | 2     |
| d    | d-1 | d-1-1  | 1     |
| d    | d-1 |        | 1     |
| d    |     |        | 1     |
|      |     |        | 3     |

但我希望它的结果在 json 中,如下所示

{
  c: {
    'total': 2,
    c-1: {
      'total': 2,
      'c-1-1': 1,
      'c-1-2': 1,
    }
  },

  d: {
    'total': 1,
    'd-1': {
      'total': 1,
      'd-1-1': 1
    }
  }
}

我已经尝试了 json_build_object 之类的方法,但我无法操作 ROLLUP 数据。非常感谢任何帮助!

这里是 link to the fiddle (PostgreSQL V10)

您需要分层查询:

with totals as (
    select main, sub, subsub, count(*) 
    from test 
    group by rollup(main, sub, subsub)
    order by main, sub, subsub
)

select jsonb_object_agg(main, sub) as main
from (
    select 
        main, 
        jsonb_object_agg(
            coalesce(sub, 'total'), 
            case when sub is null 
                then subsub->'total' 
                else subsub end
        ) as sub
    from (
        select 
            main, sub, 
            jsonb_object_agg(
                coalesce(subsub, 'total'), count
            ) as subsub
        from totals
        group by main, sub
        having main is not null
        ) s
    group by main
    ) m
where main is not null

db-fiddle.

没有cte的版本:

select jsonb_object_agg(main, sub) as main
from (
    select 
        main, 
        jsonb_object_agg(
            coalesce(sub, 'total'), 
            case when sub is null 
                then subsub->'total' 
                else subsub end
        ) as sub
    from (
        select 
            main, sub, 
            jsonb_object_agg(
                coalesce(subsub, 'total'), 
                count
            ) as subsub
        from (
            select main, sub, subsub, count(*) 
            from test 
            group by rollup(main, sub, subsub)
            ) subsub
        group by main, sub
        having main is not null
        ) sub
    group by main
    ) main
where main is not null