Search code examples
sqlarraysoracledata-conversionjson-table

How to convert a json array to relational data in oracle


I have the following json array that is a part of a json file that I am trying to convert to relational data in Oracle using the json_table function:

{ "Id" : "XXX000", 
        "elements":[
      {
         "product":{
            "prodName":"Car",
            "prodCode":"CR"
         },
         "components":[
            {
               "compName":"Toyota",
               "compCode":"BRND" 
            },
            {
               "compName":"Red",
               "compCode":"CLR"
            }
         ]
      },
      {
         "product":{
            "prodName":"Truck",
            "prodCode":"TRCK"
         },
         "components":[
            {
               "compName":"Dodge",
               "compCode":"BRND"
            },
            {
               "compName":"Blue",
               "compCode":"CLR" 
            }
         ]
      }
   ]}

Here's the query I am using to do the conversion part:

select id, 
       prdct,
       case when code = 'BRND' then val
       else '' 
       end as brnd,
       case when code = 'CLR' then val
       else '' 
       end as clr
from ary,
     json_table(car, '$'
                columns (
                          id path  '$.Id',
                          nested path '$.elements.product[*]' columns (
                                                                          prdct path  '$.prodName'
                                                                        ),
                          nested path '$.elements.components[*]' columns (
                                                                        val  path  '$.compName',
                                                                        code  path  '$.compCode'
                                                                       )
                        )
               );

The results I am getting: query results

however, the expected results should be:

ID PRDCT BRND CLR
XXX000 Car Toyota Red
XXX000 Truck Dodge Blue

How do I optimize the query to return the expected results?


Solution

  • In later Oracle versions (either Oracle 19 or 21 and later) you can use:

    select id, 
           prdct,
           brnd,
           clr
    from   ary
           CROSS APPLY JSON_TABLE(
             car,
             '$'
             COLUMNS (
               id PATH  '$.Id',
               NESTED PATH '$.elements[*]' COLUMNS (
                 prdct PATH '$.product.prodName',
                 brnd  PATH '$.components[*]?(@.compCode == "BRND").compName',
                 clr   PATH '$.components[*]?(@.compCode == "CLR").compName'
               )
             )
           );
    

    Which, for the sample data:

    CREATE TABLE ary (car BLOB CHECK (car IS JSON));
    
    INSERT INTO ary (car) VALUES ('{ "Id" : "XXX000", 
            "elements":[
          {
             "product":{
                "prodName":"Car",
                "prodCode":"CR"
             },
             "components":[
                {
                   "compName":"Toyota",
                   "compCode":"BRND" 
                },
                {
                   "compName":"Red",
                   "compCode":"CLR"
                }
             ]
          },
          {
             "product":{
                "prodName":"Truck",
                "prodCode":"TRCK"
             },
             "components":[
                {
                   "compName":"Dodge",
                   "compCode":"BRND"
                },
                {
                   "compName":"Blue",
                   "compCode":"CLR" 
                }
             ]
          }
       ]}')
    

    Outputs:

    ID PRDCT BRND CLR
    XXX000 Car Toyota Red
    XXX000 Truck Dodge Blue

    In earlier versions, if brand is always first in the array and colour always second then you can use:

    select id, 
           prdct,
           brnd,
           clr
    from   ary
           CROSS APPLY JSON_TABLE(
             car,
             '$'
             COLUMNS (
               id PATH  '$.Id',
               NESTED PATH '$.elements[*]' COLUMNS (
                 prdct PATH '$.product.prodName',
                 brnd  PATH '$.components[0].compName',
                 clr   PATH '$.components[1].compName'
               )
             )
           );
    

    Which outputs the same (if you are assured of the array elements being in the same order).

    fiddle