All we need is an easy explanation of the problem, so here it is.
I have a column of a database table that is of type JSONB
and I’m wanting to get some data from that column. For the most part the column is a flat list of key \ value pairs.
Ex:
{ s_key: 'value', s_key1: 'value', s_key2: 'value' ...etc }
However, the key I’m after contains an array of JSON data (or can be null
/nil
). So the table would be something like:
id: <UUID>,
data: {
s_key: 'value',
s_key1: 'value',
board_members: [
{first_name: 'Hugo', last_name: 'Grant', ind: true },
{first_name: 'Larry', last_name: 'Larson', ind: false },
{first_name: 'Rick', last_name: 'Flair', ind: 'true' } ]
}
created_at: <Timestamp>
Now, what I want I do is have a sub select that gives me the concat’d name string (first_name + last_name) based on the ind
(whether or not it’s true/’true’). So, I want an output of:
[ 'Hugo Grant', 'Rick Flair' ]
I’ve achieved this — to a degree — with this PSQL Snippet:
select t.id, array_agg(t._name) as _board
from (
select
d.id,
jsonb_extract_path_text(jsonb_array_elements(
case jsonb_extract_path(d.data, 'board_members')
when 'null' then '[{}]'::jsonb
else jsonb_extract_path(d.data, 'board_members')
end
), 'first_name') || ' ' || jsonb_extract_path_text(jsonb_array_elements(
case jsonb_extract_path(d.data, 'board_members')
when 'null' then '[{}]'::jsonb
else jsonb_extract_path(d.data, 'board_members')
end
), 'last_name') as _name
from my_table d
group by d.id
) t
group by t.id
Is there a way to simplify the SQL statement?
How to solve :
I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.
Method 1
You can use jsonb_path_query_array
to get all matching array elements:
jsonb_path_query_array(data, '$.board_members[*] ? (@.ind == true)')
The above returns
[
{"ind": true, "last_name": "Grant", "first_name": "Hugo"},
{"ind": true, "last_name": "Flair", "first_name": "Rick"}
]
for your sample data.
To get the concatenated first/lastname you need to unnest the array and aggregate the names back.
select id,
(select jsonb_agg(concat_ws(' ', p.item ->> 'first_name', p.item ->> 'last_name'))
from jsonb_array_elements(jsonb_path_query_array(data, '$.board_members[*] ? (@.ind == true)')) as p(item)) as names
from my_table
The above returns ["Hugo Grant", "Rick Flair"]
in the names
column
Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0