skip to Main Content

In a XML like this:

<item>
  <id>1</id>
  <name>ITEM 1</name>
  <subitems>
    <item>
      <id>2</id>
      <name>SUBITEM 1</name>
      <subitems/>
    </item>
    <item>
      <id>3</id>
      <name>SUBITEM 2</name>
      <subitems>
        <item>
          <id>4</id>
          <name>SUBITEM 3</name>
          <subitems/>
        </item>
      </subitems>
    </item>
  </subitems>
</item>

There can be several levels of subitems in the file. I can get all items (id and name) of parent and childs with this SQL:

SELECT * 
  FROM XMLTABLE ('//item' PASSING XMLTYPE (:XML) COLUMNS
                 ID VARCHAR2(5) PATH '/item/id',
                 NAME VARCHAR2(50) PATH '/item/name')

Result:

ID | NAME
------------------
1  | ITEM 1
2  | SUBITEM 1
3  | SUBITEM 2
4  | SUBITEM 3

How can I do it in the corresponding JSON below using JSON_TABLE?

{
  "id": 1,
  "name": "ITEM 1",
  "subitems": [
    {
      "id": 2,
      "name": "SUBITEM 1",
      "subitems": [
      ]
    },
    {
      "id": 3,
      "name": "SUBITEM 2",
      "subitems": [
        {
          "id": 4,
          "name": "SUBITEM 3",
          "subitems": [
          ]
        }
      ]
    }
  ]
}

In short, how to extract values from JSON without informing the full path?

4

Answers


  1. select * from json_table(
    q'~{
      "id": 1,
      "name": "ITEM 1",
      "subitems": [
        {
          "id": 2,
          "name": "SUBITEM 1",
          "subitems": [
          ]
        },
        {
          "id": 3,
          "name": "SUBITEM 2",
          "subitems": [
            {
              "id": 4,
              "name": "SUBITEM 3",
              "subitems": [
              ]
            }
          ]
        }
      ]
    }~',
    '$'
    columns (
        id NUMBER(10,0) path '$.id',
        name VARCHAR2(64) path '$.name'
    )
    )
    ;
    
    Login or Signup to reply.
  2. You can use a recursive sub-query factoring clause:

    WITH rsqfc (id, name, subitems) AS (
      SELECT j.id,
             j.name,
             j.subitems
      FROM   table_name t
             CROSS APPLY JSON_TABLE(
               t.value,
               '$'
               COLUMNS (
                 id       NUMBER       PATH '$.id',
                 name     VARCHAR2(20) PATH '$.name',
                 subitems JSON         PATH '$.subitems'
               )
             ) j
    UNION ALL
      SELECT rj.id,
             rj.name,
             rj.subitems
      FROM   rsqfc r
             CROSS APPLY JSON_TABLE(
               r.subitems,
               '$[*]'
               COLUMNS (
                 id       NUMBER       PATH '$.id',
                 name     VARCHAR2(20) PATH '$.name',
                 subitems JSON         PATH '$.subitems'
               )
             ) rj
    )
    CYCLE subitems SET is_cycle TO 1 DEFAULT 0
    SELECT id, name
    FROM   rsqfc
    

    Which, for the sample data:

    CREATE TABLE table_name (value JSON );
    
    INSERT INTO table_name (value) VALUES ('{
      "id": 1,
      "name": "ITEM 1",
      "subitems": [
        {
          "id": 2,
          "name": "SUBITEM 1",
          "subitems": []
        },
        {
          "id": 3,
          "name": "SUBITEM 2",
          "subitems": [
            {
              "id": 4,
              "name": "SUBITEM 3",
              "subitems": []
            }
          ]
        }
      ]
    }');
    

    Outputs:

    ID NAME
    1 ITEM 1
    2 SUBITEM 1
    3 SUBITEM 2
    4 SUBITEM 3

    fiddle

    Login or Signup to reply.
  3. with data(js) as (
        select 
        q'~{
          "id": 1,
          "name": "ITEM 1",
          "subitems": [
            {
              "id": 2,
              "name": "SUBITEM 1",
              "subitems": [
              ]
            },
            {
              "id": 3,
              "name": "SUBITEM 2",
              "subitems": [
                {
                  "id": 4,
                  "name": "SUBITEM 3",
                  "subitems": [
                  ]
                }
              ]
            }
          ]
        }~'
        from dual
    ),
    tdata(ids, names) as (
        select 
            json_query( cast(js as varchar2(4000)), '$..id' returning varchar2 with wrapper),
            json_query( cast(js as varchar2(4000)), '$..name' returning varchar2 with wrapper)
        from data
    )
    select id, name from (
        select rn, id from tdata,
        json_table(ids,
            '$[*]'
            columns (
                rn for ordinality,
                id PATH '$'
            ) 
        )
    ) tids
    join (
        select rn, name from tdata,
        json_table(names,
            '$[*]'
            columns (
                rn for ordinality,
                name PATH '$'
            ) 
        )
    ) tnames on tids.rn = tnames.rn
    ;
    
    1   ITEM 1
    2   SUBITEM 1
    3   SUBITEM 2
    4   SUBITEM 3
    
    Login or Signup to reply.
  4. Shortest, still based on json_query ‘$..’ syntax:

    with data(js) as (
        select 
        q'~{
          "id": 1,
          "name": "ITEM 1",
          "subitems": [
            {
              "id": 2,
              "name": "SUBITEM 1",
              "subitems": [
              ]
            },
            {
              "id": 3,
              "name": "SUBITEM 2",
              "subitems": [
                {
                  "id": 4,
                  "name": "SUBITEM 3",
                  "subitems": [
                  ]
                }
              ]
            }
          ]
        }~'
        from dual
    ),
    tdata(js) as (
        select json_object(
            'ids' value json_query( cast(js as varchar2(4000)), '$..id' returning varchar2 with wrapper),
            'names' value json_query( cast(js as varchar2(4000)), '$..name' returning varchar2 with wrapper)
        )
        from data
    )
    select 
        max(id) as id, max(name) as name
    from tdata, json_table(
        js,
        '$' 
        columns(
            nested '$.ids'
            columns (
                nested '$[*]'
                columns (
                    id_idx for ordinality,
                    id  number PATH '$'
                )
            ),
            
            nested '$.names'
            columns (
                nested '$[*]'
                columns (
                    name_idx for ordinality,
                    name  varchar2(64) PATH '$'
                )
            )
        )
    )
    group by coalesce(id_idx,name_idx) 
    ;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search