Querying nested Json object in postgres - sql

I have a jsonb column in my table and data is in this format:
[
{
"id": 1,
"DATA": {
"a": "XXX",
"key": "value1"
}
},
{
"id": 2,
"DATA": {
"a": "XXX",
"key": "value2"
}
}
]
I would like to get the count of rows in which key = value1. I tried some queries like:
select count(t.id)
from my_table t,
jsonb_array_elements(summary->'DATA') elem
where elem->>'key' = 'value1';
It returned 0 rows, though there are rows in db with that key value pair. Thanks in advance,

Use jsonb_array_elements() for the column summary as it is in the form of json array.
select count(distinct t.id)
from my_table t
cross join jsonb_array_elements(summary) elem
where elem->'DATA'->>'key' = 'value1';
Alternatively, you can get rid of the function using #> operator:
select count(t.id)
from my_table t
where summary #> '[{"DATA":{"key":"value1"}}]'
The second solution should be faster.
Db<>fiddle.

Related

Accessing a tag in an array element of a postgres table with json column

My Postgres DB Table BookingDetails has columns - id(bigint), info(jsonb)
My Json structure:
"bookingType": “special”,
“travellers”: [
{
"id": 1,
"nationality": "SomeValue",
}
],
“someTag”: “abc”
}
The travellers here is an array (and in this example, there’s only one element)
I want to:
Select records where nationality=‘CertainValue’
Update records set nationality=‘AnotherValue’ where nationality=‘CertainValue’
I could retrieve the array using:
select CAST (info->'travellers' AS TEXT) from BookingDetails
[
{
"id": 1,
"nationality": "SomeValue",
}
]
I could retrieve the array first element using:
select CAST (info->'travellers'->>0 AS TEXT) from BookingDetails
{
"id": 1,
"nationality": "SomeValue"
}
But how to do a select based on nationality=‘CertainValue’?
try this :
SELECT jsonb_set(info, array['travellers', (a.id - 1) :: text, 'nationality'], to_jsonb('AnotherValue' :: text))
FROM BookingDetails
CROSS JOIN LATERAL jsonb_array_elements(info->'travellers') WITH ORDINALITY AS a(content, id)
WHERE a.content->>'nationality' = 'CertainValue'
see the test result in dbfiddle

Postgreql json data how to list all elements of an array

I have a jsonB field in postgresql DB table, it has data like this
{
"units": [
{
"id": 299872379221376,
"unitNumber": "1",
"unitFloorSpace": 1,
"createdTimeStamp": 1587994498586
},
{
"id": 299872417011074,
"unitNumber": "2",
"unitFloorSpace": 2,
"createdTimeStamp": 1588001330085
}
]
}
I just want to list all unitNumbers like below, what would be the query for that?
1,
2,
I have tried below json query but that doesn’t list
Select form_data -> units -> unitNumbers from table where row_id =1;
here is one way:
select jsonb_array_elements(jsonb_extract_path(jdata,'units')) ->> 'unitNumber' as UnitNumber
from tableName;
db<>fiddle here

How to update a json array using the key

Let's say I have this json in my jsonb column
{
"fields": [
{
"name": "firstName",
"age": 17
},
{
"name": "lastName",
"age": 25
},
...
}
How can I update the "firstName" only without using the index?
I have this so far, but this is using the index which I don't want to use
UPDATE person
SET
field = jsonb_set(field,
concat('{fields, 0, name'}')::text[],
'new value'::jsonb,
TRUE)
You will need to unnest the array elements, replace the one you want and then aggregate them back:
update the_table tg
set data = data||(select jsonb_build_object(
'fields', jsonb_agg(case
when t.j ->> 'name' = 'firstName'
then t.j||'{"name": "new_name"}'
else t.j
end))
from jsonb_array_elements(tg.data -> 'fields') as t(j))
where ....;
Online example: https://rextester.com/BZVKD68215

Working with arrays with BigQuery LegacySQL

Each row in my table has a field that is an array, and I'd like to get a field from the first array entry.
For example, if my row is
[
{
"user_dim": {
"user_id": "123",
"user_properties": [
{
"key": "content_group",
"value": {
"value": {
"string_value": "my_group"
}
}
}
]
},
"event_dim": [
{
"name": "main_menu_item_selected",
"timestamp_micros": "1517584420597000"
},
{
"name": "screen_view",
"timestamp_micros": "1517584420679001"
}
]
}
]
I'd like to get
user_id: 123, content_group: my_group, timestamp_1517584420597000
As Elliott mentioned - BigQuery Standard SQL has way much better support for ARRAYs than legacy SQL. And in general, BigQuery team recommend using Standard SQL
So, below is for BigQuery Standard SQL (including handling wildcard stuff)
#standardSQL
SELECT
user_dim.user_id AS user_id,
(SELECT value.value.string_value
FROM UNNEST(user_dim.user_properties)
WHERE key = 'content_group' LIMIT 1
) content_group,
(SELECT event.timestamp_micros
FROM UNNEST(event_dim) event
WHERE name = 'main_menu_item_selected'
) ts
FROM `project.dataset.app_events_*`
WHERE _TABLE_SUFFIX BETWEEN '20180129' AND '20180202'
with result (for the dummy example from your question)
Row user_id content_group ts
1 123 my_group 1517584420597000

json extract multiple level value in sql

This is a follow-up question of Extract all values from json in sql table
What if the json value has multiple levels?
For example,
{
"store-1": {
"Apple": {
"category": "fruit",
"price": 100
},
"Orange": {
"category": "fruit",
"price": 80
}
},
"store-2": {
"Orange": {
"category": "fruit",
"price": 90
},
"Potato": {
"category": "vegetable",
"price": 40
}
}
}
In this case, I want to extract the price for all the items. But I get error when I run the below query.
with my_table(items) as (
values (
'{"store-1":{"Apple":{"category":"fruit","price":100},"Orange":{"category":"fruit","price":80}},
"store-2":{"Orange":{"category":"fruit","price":90},"Potato":{"category":"vegetable","price":40}}}'::json
)
)
select key, (value->value->>'price')::numeric as price
from my_table,
json_each(json_each(items))
I get the below error.
ERROR: function json_each(record) does not exist
LINE 10: json_each(json_each(items))
If I remove one json_each(), it throws
ERROR: operator does not exist: json -> json
LINE 8: select key, (value->value->>'price')::numeric as price
You can use lateral join, something like:
with my_table(items) as (
values (
'{"store-1":{"Apple":{"category":"fruit","price":100},"Orange":{"category":"fruit","price":80}},
"store-2":{"Orange":{"category":"fruit","price":90},"Potato":{"category":"vegetable","price":40}}}'::json
)
)
select outer_key, key, value->>'price' from (
select key as outer_key, value as val from my_table
join lateral json_each(items)
on true
)t
join lateral json_each(val)
on true