Вам нужен иерархический запрос:
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
дб-скрипка.
Версия без 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