如何聚合子查询中的多行?

时间:2020-07-30 08:18:44

标签: json postgresql jsonb postgresql-9.6

我想为每个prod_id获取chd_id的数组, 下面的查询有效,但仅适用于limit 1,但我希望获得准确的结果,但需要多行

select 
    prod_id,
    array_agg((select * from jsonb_array_elements(products.prod_prop) limit 1)->>'chd_id')
from products
group by prod_id
limit 10

当前结果:

enter image description here

products.prod_prop的原始值为

[{"val": ["xxx"], "chd_id": 25812}, {"val": ["yyy"], "chd_id": 2342}]

没有limit 1,我得到ERROR: more than one row returned by a subquery used as an expression

2 个答案:

答案 0 :(得分:2)

您可以在表和通过使用CROSS JOIN函数生成的子查询中使用jsonb_array_elements()

SELECT p.prod_id, array_agg(j.e->>'chd_id') AS chd_id
  FROM products p
 CROSS JOIN jsonb_array_elements(prod_prop) j(e)
 GROUP BY prod_id

答案 1 :(得分:2)

我认为您需要通过横向连接来做到这一点:

select prod_id,
       array_agg(t.chd_id)
from products p
  left join lateral (
    select e ->> 'chd_id' as chd_id
    from jsonb_array_elements(p.prod_prop) as t(e)
  ) t on true
group by prod_id
limit 10;

如果prod_prop值为空,则需要外部联接

显然,JSONB中的数据有时是数组,有时是纯对象。如果所有“普通对象”确实都是空值,则可以使用以下方法解决该问题:

select prod_id,
       array_agg(t.chd_id) filter (where t.chd_id is not null)
from products p
  left join lateral (
    select e ->> 'chd_id' as chd_id
    from jsonb_array_elements(nullif(p.prod_prop,'{}')) as t(e)
  ) t on true
group by prod_id
limit 10;

如果确实在该列中混合了普通对象和数组,则需要另一种方法:

select prod_id,
       array_agg(t.chd_id) filter (where t.chd_id is not null)
from products p
  left join lateral (
    select e ->> 'chd_id' as chd_id
    from jsonb_array_elements(case jsonb_typeof(p.prod_prop)
                                when 'array' then p.prod_prop
                                else '[]'
                              end) as t(e)
  ) t on true
group by prod_id
limit 10;

Online example