Postgres sql query to update the json set of second level key - sql

I have the postgres row as jsonb, and the value as foll{
"value": {
"apple": false,
"ball": true,
"cat": false
}
}ows
{
"value": {
"a": false,
"b": true,
"c": false
}
}
I want to the second level keys (a,b,c) something like
{
"value": {
"apple": false,
"ball": true,
"cat": false
}
}
How do I do this using postgresql
I couldn't able to find the examples to update the second level keys
expecting the updated response like{
"value": {
"apple": false,
"ball": true,
"cat": false
}
}

You can use a cte to associate the new key names with the old, and then update and aggregate with jsonb_object_agg:
with cte(o_id, n_id) as (
select 'a', 'apple'
union all
select 'b', 'ball'
union all
select 'c', 'cat'
)
update tbl set js = jsonb_build_object('value', (select jsonb_object_agg(c.n_id, v.value)
from jsonb_each(js -> 'value') v join cte c on c.o_id = v.key) || coalesce(
(select jsonb_object_agg(v.key, v.value) from jsonb_each(js -> 'value') v
where not exists (select 1 from cte c where c.o_id = v.key)), '{}'::jsonb));
See fiddle.

Related

How to Extract the Fields in Bigquery in Nested JSON

I have the following BigQuery :
select JSON_EXTRACT_SCALAR(payload, "$.payload") from mytable
It returns this result :
[
{
"productInfo": {
"productId": "123",
"productType": "Dolls"
},
"storefrontPricingList": [
{
"currentPrice": {
"unitValue": {
"currencyAmount": 10,
"currencyUnit": "USD"
},
"currentValue": {
"currencyAmount": 10,
"currencyUnit": "USD"
},
"variableUnitValue": {
"currencyAmount": 10,
"currencyUnit": "USD"
},
"sellValue": {
"currencyAmount": 10,
"currencyUnit": "USD"
},
"type": "EA"
},
"currentPriceType": "OKAY"
}
]
}
]
Now i want to access theses attributes productInfo.productId , currentPrice.unitValue.currencyAmount.
How we can access these elements i tries couple of things but all giving me null :
Like
select JSON_EXTRACT_SCALAR(payload, "$.payload[0].productInfo.productId") from mytable
select JSON_EXTRACT_SCALAR(payload, "$.payload[0].storefrontPricingList[0]. currentPrice. unitValue. currencyAmount") from mytable
Can you try this ?
-- Declaring a bigQuery variable
DECLARE json_data JSON DEFAULT (SELECT PARSE_JSON('{"payload": [{"productInfo": {"productId": "123","productType": "Dolls" },"storefrontPricingList": [{"currentPrice": {"unitValue": {"currencyAmount": 10,"currencyUnit": "USD"},"currentValue": {"currencyAmount": 10,"currencyUnit": "USD"},"variableUnitValue": {"currencyAmount": 10,"currencyUnit": "USD"},"sellValue": {"currencyAmount": 10,"currencyUnit": "USD"},"type": "EA"},"currentPriceType": "OKAY"}]}]}'));
-- Select statement for extraction
select ARRAY(
SELECT JSON_EXTRACT_SCALAR(payload, '$.productInfo.productId') from UNNEST(JSON_EXTRACT_ARRAY(json_data,"$.payload"))payload
)extracted_productID,
ARRAY(
SELECT JSON_EXTRACT_SCALAR(payload, '$.storefrontPricingList[0].currentPrice.unitValue.currencyAmount') from UNNEST(JSON_EXTRACT_ARRAY(json_data,"$.payload"))payload
)extracted_currencyAmount
Used combination of Array function and Json function for BQ.
Output:

Querying nested Json object in postgres

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.

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

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

Postgres JSONB query about nested/recursive elements

I have a nested and hierarchical structure expressed in JSON e.g.:
{
"id":1,
"children": [
{ "id":2 },
{ "id": 3, "children": [
{ "id": 4 }
]
}
]
}
Can postgres answer a query whether the record contains "id": 4 in any part of the document?
If yes, are such queries backed by JSONB indexing added in version 9.4?
UPDATE: Thanks to therealgaxbo on reddit, we started with my original code and developed something more concise:
with recursive deconstruct (jsonlevel) as(
values ('{"id":1,"children":[{"id":2},{"id":3,"children":[{"id":4}]}]}'::json)
union all
select
case left(jsonlevel::text, 1)
when '{' then (json_each(jsonlevel)).value
when '[' then json_array_elements(jsonlevel)
end as jsonlevel
from
deconstruct
where
left(jsonlevel::text, 1) in ('{', '[')
)
select * from deconstruct where case when left(jsonlevel::text, 1) = '{' then jsonlevel->>'id' = '4' else false end;
My original response below:
I experimented like crazy and finally came up with something like this:
with recursive ret(jsondata) as
(select row_to_json(col)::text jsondata from
json_each('{
"id":1,
"children": [
{ "id":2 },
{ "id": 3, "children": [
{ "id": 4 }
]
}
]
}'::json) col
union
select case when left(jsondata::text,1)='[' then row_to_json(json_each(json_array_elements(jsondata)))::text
when left((jsondata->>'value'),2)='{}' then null::text
when left((jsondata->>'value')::text,1)='[' then row_to_json(json_each(json_array_elements(jsondata->'value')))::text
else ('{"key":'||(jsondata->'key')||', "value":'||(jsondata->'value')||'}')::json::text end jsondata
from (
select row_to_json(json_each(ret.jsondata::json)) jsondata
from ret) xyz
)
select max(1) from ret
where jsondata::json->>'key'='id'
and jsondata::json->>'value'='1'