How to update each json object in json array? - sql

I have simple table table_b
id (integer)
data (json)
text (text)
1
{}
yes
2
{}
no
Json look like
{"types": [{"key": "first_event", "value": false}, {"key": "second_event", "value": false}, {"key": "third_event", "value": false}...]}
I just want to modify data and add to each json object in array ["test1", "test2"] to look like this :
{"types": [{"key": "first_event", "value": false, "can":["test1", "test2"] }, {"key": "second_event", "value": false , "can":["test1", "test2"]}, {"key": "third_event", "value": false , "can":["test1", "test2"]}...]}
ofcourse only where text like "yes"
I have tryed :
UPDATE table_b
SET data = jsonb_set(data , '{types,can}', '["test1", "test2"]'::jsonb, true)
where text like 'yes';
But it does not work. How can i loop over table_b and over data column?

The array value of a JSON object can be modified. It can be simply done by modifying the value present at a given index.

Using this sample data
create table table_b as
select * from (values
(1,'{"types": [{"key": "first_event", "value": false}, {"key": "second_event", "value": false}, {"key": "third_event", "value": false}]}'::jsonb,'yes'),
(2,'{}'::jsonb,'no'),
(3,'{"types": [{"key": "first_event", "value": false}]}'::jsonb,'yes')
) table_b(id,data,txt)
This query updates each array element, keeping the order in the ORDINALITY column
with table_b2 as (
select
id,item, index,
jsonb_set(item,('{"can"}')::TEXT[],'["test1", "test2"]') new_item
from table_b cross join
jsonb_array_elements(data -> 'types') WITH ORDINALITY arr(item, index)
where txt = 'yes')
select * from table_b2
|
id|item |index|new_item |
--+---------------------------------------+-----+------------------------------------------------------------------+
1|{"key": "first_event", "value": false} | 1|{"can": ["test1", "test2"], "key": "first_event", "value": false} |
1|{"key": "second_event", "value": false}| 2|{"can": ["test1", "test2"], "key": "second_event", "value": false}|
1|{"key": "third_event", "value": false} | 3|{"can": ["test1", "test2"], "key": "third_event", "value": false} |
3|{"key": "first_event", "value": false} | 1|{"can": ["test1", "test2"], "key": "first_event", "value": false} |
The next query concatenates the updated elements back to the array (keeping the right order) and performs a simple update
with table_b2 as (
select
id,item, index,
('{types,' || index - 1 || ',"can"}')::TEXT[] AS path,
jsonb_set(item,('{"can"}')::TEXT[],'["test1", "test2"]') new_item
from table_b cross join
jsonb_array_elements(data -> 'types') WITH ORDINALITY arr(item, index)
where txt = 'yes'),
table_b3 as (
select
id,
jsonb_agg(new_item order by index) new_data
from table_b2
group by id)
update table_b t
set data = table_b3.new_data
from table_b3
where t.id = table_b3.id
select * from table_b order by id;
id|data |txt|
--+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---+
1|[{"can": ["test1", "test2"], "key": "first_event", "value": false}, {"can": ["test1", "test2"], "key": "second_event", "value": false}, {"can": ["test1", "test2"], "key": "third_event", "value": false}]|yes|
2|{} |no |
3|[{"can": ["test1", "test2"], "key": "first_event", "value": false}] |yes|

Related

How to extract a value in a JSON table on BigQuery?

I have a JSON table which has over 30.000 rows. There are different rows like this:
JSON_columns
------------
{
"level": 20,
"nickname": "ABCDE",
"mission_name": "take_out_the_trash",
"mission_day": "150",
"duration": "0",
"properties": []
}
{
"nickname": "KLMNP",
"mission_name": "recycle",
"mission_day": "180",
"properties": [{
"key": "bottle",
"value": {
"string_value": "blue_bottle"
}
}, {
"key": "bottleRecycle",
"value": {
"string_value": "true"
}
}, {
"key": "price",
"value": {
"float_value": 21.99
}
}, {
"key": "cost",
"value": {
"float_value": 15.39
}
}]
}
I want to take the sum of costs the table. But firtsly, I want to extract the cost from the table.
I tried the code below. It returns null:
SELECT JSON_VALUE('$.properties[3].value.float_value') AS profit
FROM `missions.missions_study`
WHERE mission_name = "recycle"
My question is, how can I extract the cost values right, and sum them?
Common way to extract cost from your json is like below.
WITH sample_table AS (
SELECT '{"level":20,"nickname":"ABCDE","mission_name":"take_out_the_trash","mission_day":"150","duration":"0","properties":[]}' json
UNION ALL
SELECT '{"nickname":"KLMNP","mission_name":"recycle","mission_day":"180","properties":[{"key":"bottle","value":{"string_value":"blue_bottle"}},{"key":"bottleRecycle","value":{"string_value":"true"}},{"key":"price","value":{"float_value":21.99}},{"key":"cost","value":{"float_value":15.39}}]}' json
)
SELECT SUM(cost) AS total FROM (
SELECT CAST(JSON_VALUE(prop, '$.value.float_value') AS FLOAT64) AS cost
FROM sample_table, UNNEST(JSON_QUERY_ARRAY(json, '$.properties')) prop
WHERE JSON_VALUE(json, '$.mission_name') = 'recycle'
AND JSON_VALUE(prop, '$.key') = 'cost'
);

How to use JSON values in Oracle PL/SQL Inner Join SELECT statement

I have two tables T1 and T2. In T1 I have a column C1 that contains a value :
"businessKeys": [{
"name": "REF_ID",
"value": "2634",
"type": "Integer"
}, {
"name": "VERSION_REF_ID",
"value": "91950",
"type": "Integer"
}, {
"name": "SCENARIO",
"value": "test1",
"type": "String"
}, {
"name": "CYCLE",
"value": "2021Q3-1",
"type": "String"
}
]
In Table T2 I have a column C2 :
{
"businessKeys": [{
"name": "REF_ID",
"value": "2634",
"type": "Integer"
}, {
"name": "VERSION_REF_ID",
"value": "91950",
"type": "Integer"
}, {
"name": "SCENARIO",
"value": "test1",
"type": "String"
}, {
"name": "CYCLE",
"value": "2021Q3-1",
"type": "String"
}
],
"secondaryKeys": [{
"name": "EQUATION_ID",
"value": "Value1",
"type": "String"
}, {
"name": "EQUATION_NAME",
"value": "Value 2",
"type": "String"
}, {
"name": "USECASE",
"value": "Test Use Case",
"type": "String"
}, {
"name": "RECORD_DATE",
"value": "07-01-2023",
"type": "Date"
}, {
"name": "OUTPUT_VALUE",
"value": "0",
"type": "Float"
}
]
}
How do I get "secondaryKeys" from T2.C2 if I match "businessKeys"?
If it wasn't JSON fields I would have a simple SELECT :
SELECT t2.secondaryKeys from T1 t1, T2 t2
WHERE t1.businessKeys = t2.businessKeys
I also need to retrieve certain value from SecondaryKeys : OUTPUT_VALUE.
I assumed there's an additional column id in table t1, which you use to select a (unique?) row from t1. Notice where that goes: the where clause at the end of subquery q1 in the with clause.
This solution depends critically on the JSON structure being very rigid: the businessKeys object value is always an array with four object members with exactly those keys AND exactly those values for the key name, and similarly for secondaryKeys. These can be relaxed easily in later Oracle versions, which support filter expressions in JSON paths, the JSON_EQUAL condition, etc.; in Oracle 12.1 (and even 12.2) it would be quite a bit harder.
with
q1 (bk_ref_id, bk_version_ref_id, bk_scenario, bk_cycle) as (
select j1.bk_ref_id, j1.bk_version_ref_id, j1.bk_scenario, j1.bk_cycle
from t1 cross apply
json_table(c1, '$.businessKeys'
columns ( bk_ref_id integer path '$[0].value'
, bk_version_ref_id integer path '$[1].value'
, bk_scenario varchar2 path '$[2].value'
, bk_cycle varchar2 path '$[3].value'
)
) j1
where t1.id = 101 -------- INPUT ID GOES HERE --------
)
, q2 (bk_ref_id, bk_version_ref_id, bk_scenario, bk_cycle, sk_output_value) as (
select j2.bk_ref_id, j2.bk_version_ref_id, j2.bk_scenario, j2.bk_cycle,
j2.sk_output_value
from t2 cross apply
json_table(c2, '$'
columns
( sk_output_value number path '$.secondaryKeys[4].value'
, nested path '$.businessKeys'
columns ( bk_ref_id integer path '$[0].value'
, bk_version_ref_id integer path '$[1].value'
, bk_scenario varchar2 path '$[2].value'
, bk_cycle varchar2 path '$[3].value'
)
)
) j2
)
select q2.sk_output_value
from q1 join q2 using (bk_ref_id, bk_version_ref_id, bk_scenario, bk_cycle)
;

How to get the inner elements of JsonB column in Postgres

I have a Json like this in my JsonB column:
{
"emails": [
{
"email": {
"id": "a8399412-165e-4601-824f-a55f631ad471",
"value": "test#gmail.com"
}
},
{
"email": {
"id": "fa09d9a7-a36a-42a4-8627-66b7554ce82e",
"value": "test1#gmail.com"
}
}
],
"Address": [
{
"address": {
"id": "a8399412-165e-4601-824f-a55f631ad471",
"addressLine1": "Line1"
}
},
{
"address": {
"id": "fa09d9a7-a36a-42a4-8627-66b7554ce82e",
"addressLine2": "Line2"
}
}
],
"lastName": {
"id": "bc10a5a9-04ff-4a00-b167-ac3232e5cb89",
"value": "LastName"
},
"firstName": {
"id": "4ccdd400-2586-4a7f-9379-aff4d1f5d9d6",
"value": "FirstName"
}
}
and so on. My requirement to get list of elements as key and value pairs with limit, I did a research tried different functions of postgres and I wrote the below query :
select response.* from my_table t, jsonb_each_text(jsonb_column) as response;
If I do like this I'm getting only the root elements like emails, firstName and lastName, but I want inner elements as well along with their values like below :
Key | value
------- ---------
"email" : {"id": "a8399412-165e-4601-824f-a55f631ad471","value": "test#gmail.com"}
"email" : {"id": "fa09d9a7-a36a-42a4-8627-66b7554ce82e","value": "test1#gmail.com"}
"lastName" : {"id": "bc10a5a9-04ff-4a00-b167-ac3232e5cb89","value": "LastName"}
"firstName" : {"id": "4ccdd400-2586-4a7f-9379-aff4d1f5d9d6","value": "FirstName"}
"address" : {"id": "a8399412-165e-4601-824f-a55f631ad471", "addressLine1": "Line1"}
"address" : {"id": "a8399412-165e-4601-824f-a55f631ad471", "addressLine2": "Line2"}
You can use jsonb_array_elements() function, and combine queries by UNION ALL
SELECT 'email' AS key, je.* ->> 'email' AS value
FROM my_table
CROSS JOIN jsonb_array_elements(jsonb_column->'emails') AS je
UNION ALL
SELECT 'address', ja.* ->> 'address'
FROM my_table
CROSS JOIN jsonb_array_elements(jsonb_column->'Address') AS ja
UNION ALL
SELECT 'lastName', (jsonb_column->'lastName')::text
FROM my_table
UNION ALL
SELECT 'firstName', (jsonb_column->'firstName' )::text
FROM my_table
Demo

User selected values from JSON

When a user fills out a form in a mobile application a json is created. I load this json into a postgres database and wanting to pull is apart and select the inputs that the user has selected.
I find this hard to explain you really need to see the json and the expected results. The json looks like this...
{
"iso_created_at":"2019-06-25T14:50:59+10:00",
"form_fields":[
{
"field_type":"DateAndTime",
"mandatory":false,
"form_order":0,
"editable":true,
"visibility":"public",
"label":"Time & Date Select",
"value":"2019-06-25T14:50:00+10:00",
"key":"f_10139_64_14",
"field_visibility":"public",
"data":{
"default_to_current":true
},
"id":89066
},
{
"field_type":"Image",
"mandatory":false,
"form_order":6,
"editable":true,
"visibility":"public",
"label":"Photos",
"value":[
],
"key":"f_10139_1_8",
"field_visibility":"public",
"data":{
},
"id":67682
},
{
"field_type":"DropDown",
"mandatory":true,
"form_order":2,
"editable":true,
"visibility":"public",
"label":"Customer ID",
"value":"f_10139_35_13_35_1",
"key":"f_10139_35_13",
"field_visibility":"public",
"data":{
"options":[
{
"is_default":false,
"display_order":0,
"enabled":true,
"value":"f_10139_35_13_35_1",
"label":"27"
}
],
"multi_select":false
},
"id":86039
},
{
"field_type":"CheckBox",
"mandatory":true,
"form_order":3,
"editable":true,
"visibility":"public",
"label":"Measure",
"value":[
"f_7422_10_7_10_1",
"f_7422_10_7_10_2"
],
"key":"f_10139_1_5",
"field_visibility":"public",
"data":{
"options":[
{
"is_default":true,
"display_order":0,
"enabled":true,
"value":"f_7422_10_7_10_1",
"label":"Kg"
},
{
"is_default":true,
"display_order":0,
"enabled":true,
"value":"f_7422_10_7_10_2",
"label":"Mm"
}
],
"multi_select":true
},
"id":67679
},
{
"field_type":"ShortTextBox",
"mandatory":true,
"form_order":4,
"editable":true,
"visibility":"public",
"label":"Qty",
"value":"1000",
"key":"f_10139_9_9",
"field_visibility":"public",
"data":{
},
"id":85776
}
],
"address":"Latitude: -37.811812 Longitude: 144.971745",
"shape_id":6456,
"category_id":75673,
"id":345,
"account_id":778
}
Can anyone help me?
Expected results:
account_id | report_id | field_label | field_value
------------------------------------------------------------------------
778 | 345 | Time & Date Select | 2019-06-25T14:50:00+10:00
778 | 345 | Photos | []
778 | 345 | Customer ID | 27
778 | 345 | Measure | Kg
778 | 345 | Measure | Mm
778 | 345 | Qty | 1000
like say #Amadan, might need to hardcode each field separately instead of making a clever loop ,especially with the fields "Customer ID" "and" "Measure" or any other that requires it.
You can use the json function: json_array_elements_text, here you have a example, you can adjust to your case, i am trying with your case:
select account_id::text,report_id::text,field_label::text,
--case ""Customer ID"" and ""Measure""
case
when field_label::text='"Customer ID"' then ((todo->'data'->'options')->0->'label')::text
when field_label::text='"Measure"' then ((todo->'data'->'options')->0->'label')::text ||',' ||((todo->'data'->'options')->1->'label')::text
else
field_value::text
end as field_value
from
(
select dato->'account_id' as account_id,dato->'id' as report_id,
(json_array_elements_text(dato->'form_fields')::json)->'label' as field_label,
(json_array_elements_text(dato->'form_fields')::json)->'value' as field_value,
(json_array_elements_text(dato->'form_fields')::json) as todo
from (
select '{"iso_created_at": "2019-06-25T14:50:59+10:00", "form_fields": [
{"field_type": "DateAndTime", "mandatory": false, "form_order": 0, "editable": true, "visibility": "public", "label": "Time & Date Select", "value": "2019-06-25T14:50:00+10:00", "key": "f_10139_64_14", "field_visibility": "public", "data":
{"default_to_current": true}, "id": 89066},
{"field_type": "Image", "mandatory": false, "form_order": 6, "editable": true, "visibility": "public", "label": "Photos", "value": [], "key": "f_10139_1_8", "field_visibility": "public", "data": {}, "id": 67682},
{"field_type": "DropDown", "mandatory": true, "form_order": 2, "editable": true, "visibility": "public", "label": "Customer ID", "value": "f_10139_35_13_35_1", "key": "f_10139_35_13", "field_visibility": "public", "data": {"options": [{"is_default": false, "display_order": 0, "enabled": true, "value": "f_10139_35_13_35_1", "label": "27"}], "multi_select": false}, "id": 86039},
{"field_type": "CheckBox", "mandatory": true, "form_order": 3, "editable": true, "visibility": "public", "label": "Measure", "value": ["f_7422_10_7_10_1","f_7422_10_7_10_2"], "key": "f_10139_1_5", "field_visibility": "public", "data": {"options": [{"is_default": true, "display_order": 0, "enabled": true, "value": "f_7422_10_7_10_1", "label": "Kg"},{"is_default": true, "display_order": 0, "enabled": true, "value": "f_7422_10_7_10_2", "label": "Mm"}], "multi_select": true}, "id": 67679},
{"field_type": "ShortTextBox", "mandatory": true, "form_order": 4, "editable": true, "visibility": "public", "label": "Qty", "value": "1000", "key": "f_10139_9_9", "field_visibility": "public", "data": {}, "id": 85776}
], "address": "Latitude: -37.811812 Longitude: 144.971745", "shape_id": 6456, "category_id": 75673, "id": 345, "account_id": 778}'::json as dato) as dat
) dat2
and i get this result, similar to you:
take this example and ajust to you
regards
You need to unnest the values in form_fields and then pick the label and the value from that JSON object:
select fd.account_id,
fd.report_id,
ff.field ->> 'label' as field_label,
ff.field ->> 'value' as field_value
from form_data fd
left join jsonb_array_elements(data -> 'form_fields') as ff(field) on true;
The left join is needed to still see the row from form_data even if no form_fields is available in the main JSON column.
The above assumes a table form_data with the columns account_id, report_id and data (which contains the JSON)
Online example: https://rextester.com/RNIBSB94484

How to filter Cosmos DB data based on value of an element in an array of values Using SQL API

I have a cosmosDB collection with below Data in it.
I have to find out the data only for EVENT named ABC and its value using SQL query.
[
{
"ID": "01XXXXX",
"EVENTS": [
{
"Name": "ABC",
"Value": 0
},
{
"Name": "XYZ",
"Value": 4
},
{
"Name": "PQR",
"Value": 5
}
]
},
{
"ID": "02XXXXX",
"EVENTS": [
{
"Name": "ABC",
"Value": 1
},
{
"Name": "XYZ",
"Value": 2
},
{
"Name": "PQR",
"Value": 3
}
]
}
]
I have tried the below code but it is not working since EVENT is an array.
SELECT * FROM c where c.EVENTS.Name = 'ABC'
Is there any way to find filter out the data only with Event Name as ABC using SQL?
Try using join
SELECT c FROM c
join l in c.EVENTS
where l.Name = 'ABC'