I have JSON document as one of column in a table. Its structured as below where column name are within nested key.
{
"ACT_CASHFLOW_FE_COLS": [
{
"MCR": "MCR_1",
"COLUMN_VALUE": "UVW"
},
{
"MCR": "MCR_2",
"COLUMN_VALUE": "XYZ"
}
],
"ACT_CASHFLOW_INT_RATE": [
{
"MCR": "MCR_1",
"COLUMN_VALUE": "UVW1"
},
{
"MCR": "MCR_2",
"COLUMN_VALUE": "XYZ1"
}
],
"ACT_CASHFLOW_INP_COLS": [
{
"MCR": "MCR_1",
"COLUMN_VALUE": "UVW2"
},
{
"MCR": "MCR_2",
"COLUMN_VALUE": "XYZ2"
}
],
"ASS_CASHFLOW_FE_COLS": [
{
"MCR": "MCR_1",
"COLUMN_VALUE": "UVW3"
},
{
"MCR": "MCR_2",
"COLUMN_VALUE": "XYZ3"
}
],
"ASS_CASHFLOW_INT_RATE": [
{
"MCR": "MCR_1",
"COLUMN_VALUE": "UVW4"
},
{
"MCR": "MCR_2",
"COLUMN_VALUE": "XYZ4"
}
],
"ASS_CASHFLOW_INP_COLS": [
{
"MCR": "MCR_1",
"COLUMN_VALUE": "UVW5"
},
{
"MCR": "MCR_2",
"COLUMN_VALUE": "XYZ5"
}
]
}
I want to have relational DB output as below :
I have tried below query but it does not work
SELECT jt.*
FROM JSON_TABLE(frd.run_dtls_document,
'$' COLUMNS(
NESTED PATH '$.ACT_CASHFLOW_FE_COLS[*]'
COLUMNS(
act_cashflow_fe_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr VARCHAR2(320) PATH '$.MCR'
),
NESTED PATH '$.ACT_CASHFLOW_INT_RATE[*]'
COLUMNS(
act_cashflow_int_rate VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr VARCHAR2(320) PATH '$.MCR'
),
NESTED PATH '$.ACT_CASHFLOW_INP_COLS[*]'
COLUMNS(
act_cashflow_inp_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr VARCHAR2(320) PATH '$.MCR'
),
NESTED PATH '$.ASS_CASHFLOW_FE_COLS[*]'
COLUMNS(
ass_cashflow_fe_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr VARCHAR2(320) PATH '$.MCR'
),
NESTED PATH '$.ASS_CASHFLOW_INT_RATE[*]'
COLUMNS(
ass_cashflow_int_rate VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr VARCHAR2(320) PATH '$.MCR'
),
NESTED PATH '$.ASS_CASHFLOW_INP_COLS[*]'
COLUMNS(
ass_cashflow_inp_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr VARCHAR2(320) PATH '$.MCR'
)
)) jt;
Any suggestion will be appreciated.
You can use successive cross joins among individual JSON_TABLE statements, and then filter out by mcr columns which are named differently to get rid of ambiguity such as
WITH j AS
(
SELECT *
FROM frd f, --> assuming frd is the table
JSON_TABLE(run_dtls_document, --> and this is the JSON type column of it
'$' COLUMNS (
NESTED PATH '$.ACT_CASHFLOW_FE_COLS[*]'
COLUMNS(
act_cashflow_fe_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr VARCHAR2(320) PATH '$.MCR'
)
)
) j1,
JSON_TABLE(run_dtls_document,
'$' COLUMNS (
NESTED PATH '$.ACT_CASHFLOW_INT_RATE[*]'
COLUMNS(
act_cashflow_int_rate VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr2 VARCHAR2(320) PATH '$.MCR'
)
)
) j2,
JSON_TABLE(run_dtls_document,
'$' COLUMNS (
NESTED PATH '$.ACT_CASHFLOW_INP_COLS[*]'
COLUMNS(
act_cashflow_inp_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr3 VARCHAR2(320) PATH '$.MCR'
)
)
) j3,
JSON_TABLE(run_dtls_document,
'$' COLUMNS (
NESTED PATH '$.ASS_CASHFLOW_FE_COLS[*]'
COLUMNS(
ass_cashflow_fe_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr4 VARCHAR2(320) PATH '$.MCR'
)
)
) j4,
JSON_TABLE(run_dtls_document,
'$' COLUMNS (
NESTED PATH '$.ASS_CASHFLOW_INT_RATE[*]'
COLUMNS(
ass_cashflow_int_rate VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr5 VARCHAR2(320) PATH '$.MCR'
)
)
) j5,
JSON_TABLE(run_dtls_document,
'$' COLUMNS (
NESTED PATH '$.ASS_CASHFLOW_INP_COLS[*]'
COLUMNS(
ass_cashflow_inp_cols VARCHAR2(320) PATH '$.COLUMN_VALUE',
mcr6 VARCHAR2(320) PATH '$.MCR'
)
)
) j6
)
SELECT mcr,
act_cashflow_fe_cols, act_cashflow_int_rate, act_cashflow_inp_cols,
ass_cashflow_fe_cols, ass_cashflow_int_rate, ass_cashflow_inp_cols
FROM j
WHERE mcr = mcr2
AND mcr = mcr3
AND mcr = mcr4
AND mcr = mcr5
AND mcr = mcr6
Demo
Related
I have a JSON like this (see the test setup below)
{
"dt" :
[
{
"values" :
[
{
"key" : "a"
},
{
"key" : "b"
}
]
}
]
}
and it is straightforeward to parse the inner array as it has keys as follows
SELECT tab.id,
jt.*
FROM parse_json_array tab,
json_table(data, '$.dt[*]'
COLUMNS (NESTED PATH '$.values[*]' COLUMNS(
key PATH '$.key' )
)) AS "JT"
where tab.id = 1;
which returns
ID, KEY
--------
1 a
1 b
But if the inner array has no keys, how could I addapt the path in NESTED PATH?
{
"dt" :
[
{
"values" :
[
"a",
"b"
]
}
]
}
All my try such as key PATH '$.*' or key PATH '*' return null or syntax error.
Note I do not need a solution, that parse both variants, but it would be of course a bonus;)
I'm on XE 18.4.0.0.0
Test data
create table parse_json_array
(id int primary key,
data CLOB constraint c1 check(data is JSON)
);
insert into parse_json_array (id, data) values (1, '{ "dt" : [ {"values" : [{"key" : "a"} , {"key" : "b" } ]} ] }');
insert into parse_json_array (id, data) values (2, '{ "dt" : [ {"values" : [ "a" , "b" ]}] }');
This will give you the id and the values within the nested array, when it's just an array of scalars rather than objects.
SELECT tab.id,
jt.*
FROM parse_json_array tab,
json_table(data, '$.dt[*].values[*]'
COLUMNS key PATH '$' )
AS "JT"
where tab.id = 2;
Storing JSON in both formats, and even more so, asking for a solution that works for both, doesn't make a lot of sense; the JSON structure is different. It's like asking for a SQL SELECT query that works for two different tables with different column sets.
If you need a solution with nested path (perhaps because you must pick out additional bits of data, which you did not share with us), you can do something like this (which is what Padders suggested in a comment):
SELECT tab.id,
jt.*
FROM parse_json_array tab,
json_table(data, '$.dt[*]' columns(
nested path '$.values[*]'
COLUMNS (key PATH '$' )) )
AS "JT"
where tab.id = 2;
EDIT:
To get values both from object members and from scalar members of the nested array, you can do something like this. Use nvl(k_token, token) if you just need the value and don't need to know if it comes from an array of objects or an array of scalars. Note that this solution will work even if you have objects and scalars mixed together in the same JSON (in the same nested array).
select p.id, j.k_token, j.token
from parse_json_array p,
json_table(data, '$.dt[*].values[*]'
columns( k_token path '$.key',
token path '$'
)
) j
;
column contain value given below.
[
{
"bActive": false,
"sSubLocation": "",
"aiSeries": [],
"iUser": "1"
},
{
"bActive": true,
"sSubLocation": "Mytestcase",
"aiSeries": [],
"iUser": "1"
}
]
I want to get result as sSubLocation key where it have bActive =true and sSubLocation = "Mytestcase";
SELECT test.id, jsontable.*
FROM test
CROSS JOIN JSON_TABLE(test.value,
'$[*]' COLUMNS (bActive BOOLEAN PATH '$.bActive',
sSubLocation VARCHAR(255) PATH '$.sSubLocation',
aiSeries JSON PATH '$.aiSeries',
iUser VARCHAR(255) PATH '$.iUser')) jsontable
HAVING bActive = true
AND sSubLocation = 'Mytestcase'
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=bcf7f238e23a2c282cdea76c183ae8fa
I have the following code:
select tab.*
from (SELECT '{ "name":"John",
"age":30,
"cars": [
{"name":"Ford", "models":[ "Fiesta", "Focus", "Mustang"]},
{"name":"BMW", "models":[ "320", "X3", "X5"]},
{"name":"Fiat", "models":[ "500", "Panda"]}
]}' AS DATAx FROM dual),
JSON_TABLE(DATAx, '$'
columns ("A_json" varchar2(100) path '$.age'
,"B_check" varchar2(100) path '$.name'
,"C_check" varchar2(100) path '$.cars[*].name')) tab
which shows the following table when run:
A_json: 30
B_check: John
C_check: NULL
I would like to obtain all names from the cars array. How could I get the following output instead?:
A_json: 30
B_check: John
C_check: Ford
A_json: 30
B_check: John
C_check: BMW
A_json: 30
B_check: John
C_check: Fiat
If I try replacing * for indexes (1,2,3) it works for one row only, but I am not able to get them all.
Nested path needs to be defined to reach to 'cars--> name'. try the below given SQL -
select tab.*
from (SELECT '{ "name":"John",
"age":30,
"cars": [
{"name":"Ford", "models":[ "Fiesta", "Focus", "Mustang"]},
{"name":"BMW", "models":[ "320", "X3", "X5"]},
{"name":"Fiat", "models":[ "500", "Panda"]}
]}' AS DATAx FROM dual),
JSON_TABLE(DATAx, '$'
columns ("A_json" varchar2(100) path '$.age'
,"B_check" varchar2(100) path '$.name'
,"C_check" varchar2(100) path '$.cars.name'
, nested path '$.cars[*]' columns(
name number path '$.name'))
) tab
I'm trying to parse JSON attribute with value longer than 4000 characters. This is my sample json:
[{
"id": "268edbb5d111",
"name": "Sample Product",
"status": "created",
"description": "this is sample product",
"productCharacteristic": [{
"name": "property_1",
"value": "abc",
"valueType": "String"
}, {
"name": "property_2",
"value": 123,
"valueType": "Number"
}, {
"name": "property_3",
"value": "2020-05-01T04:56:07.000+00:00",
"valueType": "Date"
}
]
}
]
and this is PL/SQL code:
FOR x IN (SELECT *
FROM json_table(l_response, '$[*]'
COLUMNS
id varchar2(255) PATH '$.id',
name varchar2(255) PATH '$.name',
description varchar2(255) PATH '$.description',
status varchar2(255) PATH '$.status',
productCharacteristic varchar2(4000) FORMAT JSON PATH '$.productCharacteristic'
)
) LOOP
-- do something
END LOOP;
The productCharacteristic attribute might be longer than 4000 but changing its datatype to VARCHAR2(32000) compiles the package with ORA-00910: specified length too long for its datatype and changing to CLOB returns ORA-40484: invalid data type for JSON_TABLE column. Is there maybe a way to pass the whole array of productCharacteristic as JSON and parse it inside of the loop?
Thanks.
Is there maybe a way to pass the whole array of productCharacteristic as JSON and parse it inside of the loop?
It might not be quite what you want, but you can get all the array elements flattened out with nested path:
FOR x IN (SELECT *
FROM json_table(l_response, '$[*]'
COLUMNS
id varchar2(255) PATH '$.id',
name varchar2(255) PATH '$.name',
description varchar2(255) PATH '$.description',
status varchar2(255) PATH '$.status',
nested path '$.productCharacteristic[*]'
COLUMNS (
product_name varchar2(255) PATH '$.name',
product_value varchar2(4000) PATH '$.value',
product_value_type varchar2(255) PATH '$.valueType'
)
)
) LOOP
-- do something
dbms_output.put_line(x.id || ' ' || x.product_name || ' ' || x.product_value);
END LOOP;
db<>fiddle
I'm trying to figure out a JSON_SET() query that would add {key2: 2} to all elements of $.a:
{
"a": [
{
"key1": 1
},
{
"key1": 1
}
]
}
Obviously, this fails because of '*' in the path:
UPDATE table SET json=JSON_SET(json, '$a[*].key2', 2);
How can this be done?
Thanks!
One option is JSON_TABLE and JSON_ARRAYAGG:
UPDATE `table`
INNER JOIN (
SELECT
JSON_ARRAYAGG(
JSON_SET(`der`.`_json`, '$.key2', 2)
) `json_key2`
FROM
`table`,
JSON_TABLE(`table`.`json`,
'$.a[*]'
COLUMNS(
`_json` JSON PATH '$'
)
) `der`
GROUP BY
`der`.`_json`
) `der`
SET `table`.`json` = JSON_SET(
`table`.`json`,
'$.a',
`der`.`json_key2`
);
See db-fiddle.