I have a jsonb column called info that some type have the form of an object: { id: 2 }
and some other types it has the shape of an array: [{ id: 2 }]
I have a query that does this:
SELECT * FROM users
CROSS JOIN LATERAL jsonb_array_elements(users.info)
Now, if my data set has just arrays in the info column, there is no issue, but if there is one that is not an array, but rather an object, I get the error:
ERROR: cannot extract elements from an object
Is there a way in PostgreSQL for that CROSS JOIN LATERAL to ignore the rows that are not arrays in the info column?
Use the function jsonb_typeof() to eliminate rows with non-array jsonb column:
select *
from (
select *
from users
where jsonb_typeof(info) = 'array'
) s
cross join lateral jsonb_array_elements(info);
Working example in rextester.
Related
I have a postgresql data with values (it is jsonb type column):
SELECT data FROM orders;
[
{
"food_id": "1",
"table": "A12",
},
{
"food_id": "2",
"table": "A14",
}
]
I can easily SELECT by providing data as it is, but how to convert it into simplified ?
My expected result:
SELECT ??? as food_tables FROM orders;
["A12", "A14"]
I personally still did not understand how jsonb_array_elements() works.
Thanks!
If you are using Postgres 12 or later, you can use jsonb_path_query_array()
select jsonb_path_query_array(data, '$[*].table') as food_tables
from orders
You could perform a lateral cross join with the unnested array elements and extract the attributes:
SELECT jsonb_agg(d.elem -> 'table')
FROM orders
CROSS JOIN LATERAL jsonb_array_elements(orders.data) AS d(elem)
GROUP BY orders.id;
Use array_agg instead of jsonb_agg if you want a PostgreSQL array.
It is a mistake to model tabular data as a JSON array. Change your data model so that each array element becomes a row in a database table.
I have a table called users that has a jsonb column called 'history'. This is an array of objects, one of the elements is called uid which is the id of the person visiting the page as follows:
[ {"ip":"...","uid":2} , {"ip":"...","uid":4} , ... ]
I'm running a query that appends the jsonb object with the field uname to make understanding who 'uid' is a bit easier which will produce:
[ {"ip":"...","uid":2,"uname":"bob"} , {"ip":"...","uid":4,"uname":"dave"} , ... ]
I'm currently doing this using the following query (say, where uid=2):
SELECT json_agg(history2||jsonb_build_object('uname',uname::text)) FROM
(SELECT jsonb_array_elements(history) AS history2 FROM users WHERE uid=2) AS table1
LEFT JOIN users AS table2 ON history2->>'uid'=table2.uid
I'm using the subquery to return a table of json objects that's then joined to the user table again to get the username.
My question is: Is there a way of doing this without having the subquery? I've read that lateral joins could be used but all my attempts at this don't seem to work.
Thanks in advance.
You can move jsonb_array_elements into the FROM clause with an outer join:
SELECT jsonb_agg(h.item||jsonb_build_object('uname', u.uname))
FROM users u
LEFT JOIN jsonb_array_elements(u.history) as h(item) on h.item ->> 'uid' = u.uid::text
WHERE u.uid = 2
I have a details table with adeet column defined as jsonb[]
a sample value stored in adeet column is as below image
Sample data stored in DB :
I want to return the rows which satisfies id=26088 i.e row 1 and 3
I have tried array operations and json operations but it does'nt work as required. Any pointers
Obviously the type of the column adeet is not of type JSON/JSONB, but maybe VARCHAR and we should fix the format so as to convert into a JSONB type. I used replace() and r/ltrim() funcitons for this conversion, and preferred to derive an array in order to use jsonb_array_elements() function :
WITH t(jobid,adeet) AS
(
SELECT jobid, replace(replace(replace(adeet,'\',''),'"{','{'),'}"','}')
FROM tab
), t2 AS
(
SELECT jobid, ('['||rtrim(ltrim(adeet,'{'), '}')||']')::jsonb as adeet
FROM t
)
SELECT t.*
FROM t2 t
CROSS JOIN jsonb_array_elements(adeet) j
WHERE (j.value ->> 'id')::int = 26088
Demo
You want to combine JSONB's <# operator with the generic-array ANY construct.
select * from foobar where '{"id":26088}' <# ANY (adeet);
I have two jsonb fields in the table below and I would like to do a query where I filter any key of the dictionary.
My problem is that those dictionaries are inside of a list and when I try to access them with:
SELECT *
FROM public.monitoring_environmentalcontrol
WHERE celery_status->'queue'='0'
I get nothing:
You can use jsonb_array_elements DOC function to achieve your goal plus a LATERAL JOIN 7.2.1.5. LATERAL Subqueries on it:
This is the setup I created:
create table test (
id int,
celery_status jsonb
);
insert into test values
(1,'[{"queue":"a"}, {"queue":"b"}, {"queue":"c"}]'),
(2,'[{"queue":"d"}, {"queue":"e"}, {"queue":"f"}]'),
(3,'[{"queue":"g"}, {"queue":"h"}, {"queue":"i"}]');
This is the query:
select t.id, t.celery_status, obj->>'queue'
from test t
join lateral
jsonb_array_elements(t.celery_status) obj(value) on obj->>'queue' = 'a'
You can see it working here: http://sqlfiddle.com/#!17/bf7bf/6
I have two tables which look like such
Organizations
Id (primary_key. big int)
Name (text)
CustomInformations
Id (primary_key. big int)
ConnectedIdentifiers (JSONB)
Info (text)
CustomInformations's ConnectedIdentifiers column contains a JSONB structure which looks like
{ 'organizations': [1,2,3] }
This means that there's an array of organizations with those ids 1, 2, 3, which are related to that particular CustomInformation
I'm trying to do a JOIN where given a CustomInformation Id will also get me all the Organizations names
I tried this after looking at some examples:
SELECT * FROM CustomInformations ci
INNER JOIN Organizations o on jsonb_array_elements(ci.ConnectedIdentifiers->'19') = o.id
WHERE
ci.id = 5
I got an error No operator matches the given name and argument type(s). You might need to add explicit type casts.
Is this the right approach? And if so what is wrong with my syntax?
Thanks
You cannot use jsonb_array_elements() in this way because the function returns set of rows. It should be placed in a lateral join instead. Use jsonb_array_elements_text() to get array elements as text and cast these elements to bigint:
select ci.*, o.*
from custominfo ci
-- lateral join
cross join jsonb_array_elements_text(ci.connectedidentifiers->'organizations') ar(elem)
join organizations o
on elem::bigint = o.id
where ci.id = 5