I'm trying map a JSON array of objects to relational table columns using JSON_TABLE.
The Parts array consists of arrays of instruction arrays. Each instruction array contains objects with consistent names of three name value pairs. The values of the name field are always "Family", "Feature", or "Price". These are not always in a consistent order within the array. I thought I could use a filter condition within the columns clauses to map the correct value to the the correct column.
WITH STR AS (
select
'
{
"Parts": [
{
"instruction": [
{
"name": "Family",
"value": "AJE",
"type": "0"
},
{
"name": "Feature",
"value": "AJKA",
"type": "0"
},
{
"name": "Price",
"value": "0",
"type": "0"
}
]
},
{
"instruction": [
{
"name": "Feature",
"value": "AJKB",
"type": "0"
},
{
"name": "Family",
"value": "AJA",
"type": "0"
}
]
}
]
}
' JSTR
FROM DUAL)
SELECT JT.*
FROM STR SO,
JSON_TABLE(SO.JSTR, '$.Parts[*].instruction[*]'
COLUMNS ("Family" PATH '$."value"?(@.name == "Family" )',
"Feature" PATH '$."value"?(@.name == "Feature")',
"Price" PATH '$."value"?(@.name == "Price")'
)
)
AS "JT"
As the code is displayed, it returns five rows with all of the fields null. I realize the code above is wrong. Below is another attempt. This code will return the data (filter condition commented out and array indexes hard coded), but the column data is mapped wrong.
JSON_TABLE(SO.JSTR, '$.Parts[*]' --ERROR ON ERROR
COLUMNS ("Family" PATH '$.instruction[0]."value"', --?(@.name == "Family" )
"Feature" PATH '$.instruction[1]."value"', --?(@.name == "Feature")
"Price" PATH '$.instruction[2]."value"' --?(@.name == "Price")
)
Family Feature Price
------ ------- -----
AJE AJKA 0
AJKA AJE
I'm trying to return this:
Family Feature Price
------ ------- -----
AJE AJKA 0
AJA AJKB
UPDATE : ORD should count the instructions, not the individual values.
To put the values you want in the right columns, adjust your path:
SELECT JT.*
FROM STR SO,
JSON_TABLE(
SO.JSTR, '$.Parts[*].instruction'
COLUMNS (
ord for ordinality,
nested path '$[*]' columns (
Family path '$?(@.name == "Family").value',
Feature path '$?(@.name == "Feature").value',
Price path '$?(@.name == "Price").value'
)
)
)
AS "JT";
ORD FAMILY FEATURE PRICE
1 AJE
1 AJKA
1 0
2 AJKB
2 AJA
Now just group by ORD:
SELECT ord,
max(family) family,
max(feature) feature,
max(price) price
FROM STR SO,
JSON_TABLE(
SO.JSTR, '$.Parts[*].instruction'
COLUMNS (
ord for ordinality,
nested path '$[*]' columns (
Family path '$?(@.name == "Family").value',
Feature path '$?(@.name == "Feature").value',
Price path '$?(@.name == "Price").value'
)
)
)
AS "JT"
group by ord
order by ord;
ORD FAMILY FEATURE PRICE
1 AJE AJKA 0
2 AJA AJKB
Best regards, Stew Ashton
One option would be determine Ordinality
values for NESTED PATH '$."Parts"[*]."instruction"[*]'
within the subquery to be used in Ordering of ROW_NUMBER()
function grouped by name
column's values, and Pivoting
by Family
and Feature
columns:
SELECT "Family", "Feature" FROM
(
SELECT value, name , ROW_NUMBER() OVER (PARTITION BY name ORDER BY rn) AS rn
FROM STR,
JSON_TABLE(jstr, '$'
COLUMNS (NESTED PATH '$."Parts"[*]."instruction"[*]'
COLUMNS (rn FOR ORDINALITY,
value VARCHAR2 PATH '$."value"',
name VARCHAR2 PATH '$."name"')
)) )
PIVOT
(
MAX(value) FOR name IN ('Family' AS "Family", 'Feature' AS "Feature")
)
ORDER BY rn
I wish StackOverflow folks would always indicate their Oracle Database version up front.
Anyway, this hack appears to work in version 18c:
SELECT ord,
max(json_value(jt.Family, '$.value')) family,
max(json_value(jt.Feature, '$.value')) feature,
max(json_value(jt.Price, '$.value')) price
FROM STR SO,
JSON_TABLE(
SO.JSTR, '$.Parts[*].instruction'
COLUMNS (
ord for ordinality,
nested path '$[*]' columns (
Family format json path '$?(@.name == "Family")',
Feature format json path '$?(@.name == "Feature")',
Price format json path '$?(@.name == "Price")'
)
)
)
AS "JT"
group by ord
order by ord;
A better solution would be to simplify the JSON and use the PIVOT clause to manipulate the result:
select family, feature, price
from (
SELECT jt.ord, jt.name, jt.value
from str,
JSON_TABLE(
str.JSTR, '$.Parts[*].instruction'
COLUMNS (
ord for ordinality,
nested path '$[*]' columns (
name varchar2(100) path '$.name',
value varchar2(100) path '$.value'
)
)
)
AS "JT"
)
pivot(max(value) for name in ('Family' as Family, 'Feature' as Feature, 'Price' as Price))
order by ord;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.