0

I want to get array of chd_id's for each prod_id, query below works but only with limit 1 i want exact result but with multiple rows

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

current result:

enter image description here

Raw value of products.prod_prop is

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

Without limit 1 i get ERROR: more than one row returned by a subquery used as an expression

2 Answers 2

3

I think you need to do this with a lateral join:

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;

The outer join is needed in case there are empty prod_prop values

Apparently the data in the JSONB is sometimes an array and sometimes a plain object. If all "plain objects" are indeed empty values, you can work around that using:

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;

If you do indeed mix plain objects and arrays in that column, you need a different approach:

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

Sign up to request clarification or add additional context in comments.

6 Comments

I got ERROR: cannot extract elements from an object
@Sojtin: it works with the sample data you have given us: dbfiddle.uk/… apparently you haven't told us everything
default value of prod_prop is '{}'::jsonb is that any relevant?
Yes, see my edit. But if your json is supposed to store an array, the default value should be '[]'::jsonb
@Sojtin: the version with the CASE expression should be able to handle any "mess"
|
2

You can use a CROSS JOIN among the table and the subquery generated through use of jsonb_array_elements() function :

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

2 Comments

I got ERROR: cannot extract elements from an object
there's no problem @Sojtin . Check this out please.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.