简体   繁体   中英

JSON_TABLE Filter Condition Issue

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

Demo

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM