【问题标题】:Parse json through json_table in oracle 18oracle 18中通过json_table解析json
【发布时间】:2021-05-26 07:52:06
【问题描述】:

有一个请求例如:

with j (sJson) as (
   select '{
      "ID":"1444284517",
      "ID_ORD":"4255;2187606199",
      "Vals":{
               "CODE":"ONB2B3BB8",
               "DORD":"25.04.2021"
             }
   }'
   from dual
)

select jt.*
from j
cross apply json_table (j.sJson, '$'
   columns
      ID varchar2(32) path '$.ID',
      ID_ORD varchar2(32) path '$.ID_ORD',
        nested path '$.Vals[*]'
              columns (
                 CODE varchar2(9) path '$.CODE',
                 DORD varchar2(30) path '$.DORD',
                 ....
              )) jt    

column 中可能有不同的字段。
如何在不指定类型和路径的情况下列出columns 中的所有字段?那是如何使它成为动态解析器​​呢?需要放弃CODE varchar2(9) path '$.CODE'
我期待这个结果:

| ID         | ID_ORD          | CODE      | DORD       |
+------------+-----------------+-----------+------------+
| 1444284517 | 4255;2187606199 | ONB2B3BB8 | 25.04.2021 | 

【问题讨论】:

  • 请编辑您的问题以显示您想要从该示例中获得的输出;并取决于您所说的动态,不同的示例及其预期输出。
  • @AlexPoole,我改了问题
  • 列中可能有不定数量的字段{ }
  • 在执行查询之前必须知道输出列的数量,所以我认为这可能与静态 SQL 无关。 (它在概念上类似于具有未知列数的枢轴。)动态 SQL 必须通过解析 JSON 来构建 columns 子句,这似乎有点痛苦。

标签: sql json oracle parsing oracle18c


【解决方案1】:

你可以定义函数:

CREATE FUNCTION get_keys(
  value IN CLOB
) RETURN SYS.ODCIVARCHAR2LIST PIPELINED
IS
  js   JSON_OBJECT_T := JSON_OBJECT_T( value );
  keys JSON_KEY_LIST;
BEGIN
  keys := js.get_keys();
  FOR i in 1 .. keys.COUNT LOOP
    PIPE ROW ( keys(i) );
  END LOOP;
END;
/

CREATE FUNCTION get_value(
  value IN CLOB,
  path  IN VARCHAR2
) RETURN VARCHAR2
IS
  js JSON_OBJECT_T := JSON_OBJECT_T( value );
BEGIN
  RETURN js.get_string( path );
END;
/

然后使用查询:

WITH j (sJson) as (
   select '{
      "ID":"1444284517",
      "ID_ORD":"4255;2187606199",
      "Vals":{
               "CODE":"ONB2B3BB8",
               "DORD":"25.04.2021"
             }
   }'
   from dual
)
SELECT jt.id,
       jt.id_ord,
       k.COLUMN_VALUE AS Key,
       get_value( jt.vals, k.COLUMN_VALUE ) AS value
FROM   j
       CROSS APPLY JSON_TABLE(
         j.sjson,
         '$'
         COLUMNS (
           id     VARCHAR2(20) PATH '$.ID',
           id_ord VARCHAR2(30) PATH '$.ID_ORD',
           vals   VARCHAR2(4000) FORMAT JSON PATH '$.Vals'
         )
       ) jt
       CROSS APPLY get_keys( jt.vals ) k

哪些输出:

ID ID_ORD KEY VALUE
1444284517 4255;2187606199 CODE ONB2B3BB8
1444284517 4255;2187606199 DORD 25.04.2021

(注意:SQL 不支持动态列数,因此您需要提供固定数量的列,例如 keyvalue,并将输出作为行而不是列。) em>

db小提琴here

【讨论】:

    【解决方案2】:

    如果您真的想要可变数量的列,那么您需要对基于列的结果进行动态透视(例如来自@MTO 的答案),或者生成动态的json_table

    您可以使用json_dataguide() 为特定的Vals 数组生成架构,然后将其传递给它自己的json_table 并循环生成columns 子句名称、类型和路径。

    此示例重新生成您的原始查询,但它使用 JSON 字符串的绑定变量而不是 CTE;然后使用与数据指南相同的字符串打开它。

    create or replace function dynamic_parse(sJson clob)
    return sys_refcursor as
      sGuide clob;
      sSQL clob;
      rc sys_refcursor;
    begin
      -- initial static part of query
      sSQL := q'^select jt.*
    from json_table (:sJson, '$'
       columns
          ID varchar2(32) path '$.ID',
          ID_ORD varchar2(32) path '$.ID_ORD',
            nested path '$.Vals[*]'
                  columns (^';
    
      select json_dataguide(jt.vals)
      into sGuide
      from json_table (sJson, '$'
        columns
          VALS clob format json path '$.Vals'
      ) jt;
    
      for r in (
        select jt.*
        from json_table (sGuide format json, '$[*]'
          columns
            indx for ordinality,
            path varchar2(30) path '$."o:path"',
            type varchar2(30) path '$.type',
            length number path '$."o:length"'
        ) jt
      )
      loop
        sSQL := sSQL || case when r.indx > 1 then ',' end
          || chr(10) || '                 '
          || '"' || substr(r.path, 3) || '"'
          -- may need to handle other data type more carefully too
          || ' ' || case when r.type = 'string' then 'varchar2(' || r.length || ')' else r.type end
          || q'^ path '^' || r.path || q'^'^';
      end loop;
    
      -- final static part of query
      sSQL := sSQL || chr(10) || '              )) jt';
      dbms_output.put_line(sSQL);
    
      open rc for sSQL using sJson;
      return rc;
    end;
    /
    

    db<>fiddle 显示了一些步骤、生成的动态 SQL 语句以及如何使用它来打开引用游标。生成的动态语句如下:

    select jt.*
    from json_table (:sJson, '$'
       columns
          ID varchar2(32) path '$.ID',
          ID_ORD varchar2(32) path '$.ID_ORD',
            nested path '$.Vals[*]'
                  columns (
                     "CODE" varchar2(16) path '$.CODE',
                     "DORD" varchar2(16) path '$.DORD'
                  )) jt
    

    它还显示了一个虚拟匿名块,该块调用该函数并打印引用游标内容(因为 dbfiddle 不支持您可以在 SQL Developer 等中执行的select func_returning_ref_cursor from dual):

    1444284517:4255;2187606199:ONB2B3BB8:25.04.2021
    

    ...但这说明了这种方法的一个问题:调用者必须提前知道列的数量和类型,或者它自己必须使用一些动态处理元素。

    您可能还想探索更广泛的 JSON Data Guide 功能。

    【讨论】:

      【解决方案3】:

      SQL/JSON 函数 json_table 将特定的 JSON 数据投影到各种 SQL 数据类型的列。您可以使用它将部分 JSON 文档映射到新虚拟表的行和列中,您也可以将其视为内联视图。

      因为您的目标似乎是:通过 json_table 解析 json。我建议阅读 JSON 数据指南功能。here。 e;g 您可以根据行的结构创建视图,DBMS_JSON 包将为您创建 SQL/JSON。无需编码。您还可以将虚拟列添加到原始表中。

      用您的文档创建一个表格,

      我添加了一个新字段 XARR 以展示 JSON 数据指南功能的强大功能

      drop table test_json;
      
      create table test_json
      as
      with j (sJson ) as (
         select CAST ('{
            "ID":"1444284517",
            "ID_ORD":"4255;2187606199",
            "Vals":{
                     "CODE":"ONB2B3BB8",
                     "DORD":"25.04.2021",
                     "XARR":[{"IDARR":1},{"IDARR":2},{"IDARR":3}]
                   }
         }' as VARCHAR2(2000))
         from dual
      )
      select *
      from j;
      

      在您的文档之上创建一个视图,

      SELECT json_dataguide(SJSON, DBMS_JSON.FORMAT_HIERARCHICAL, DBMS_JSON.PRETTY)
            FROM TEST_JSON;
      
      drop view MYVIEW;
      DECLARE
        dg CLOB;
        BEGIN
          SELECT json_dataguide(SJSON, DBMS_JSON.FORMAT_HIERARCHICAL, DBMS_JSON.PRETTY)
            INTO dg
            FROM TEST_JSON where rownum < 2;
          DBMS_JSON.create_view('MYVIEW',
                                'TEST_JSON',
                                'SJSON',
                                dg);
        END;
      /
      

      查询数据

      直接使用视图或检索生成的 SQL

      select text from all_views where view_name =  'MYVIEW';      
      
      TEXT                                                                                                                                                                                                                                                                                                                                                         
      -------------------------------------------------------------------
      SELECT RT."SJSON",JT."ID",JT."CODE",JT."DORD",JT."ID_ORD",JT."IDARR"
      FROM "ADMIN"."TEST_JSON" RT,
      JSON_TABLE("SJSON", '$[*]' COLUMNS 
      "ID" varchar2(16) path '$.ID',
      "CODE" varchar2(16) path '$.Vals.CODE',
      "DORD" varchar2(16) path '$.Vals.DORD',
       NESTED PATH '$.Vals.XARR[*]' COLUMNS (
      "IDARR" number path '$.IDARR'),
      "ID_ORD" varchar2(16) path '$.ID_ORD')JT 
      
      select * from myview;
      
      ID         CODE      DORD       ID_ORD          IDARR 
      ---------- --------- ---------- --------------- --- 
      1444284517 ONB2B3BB8 25.04.2021 4255;2187606199   1 
      1444284517 ONB2B3BB8 25.04.2021 4255;2187606199   2 
      1444284517 ONB2B3BB8 25.04.2021 4255;2187606199   3
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-14
        • 2023-03-23
        • 2019-07-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多