【问题标题】:Generically parse JSON from CLOB in oracle在 oracle 中从 CLOB 中通用解析 JSON
【发布时间】:2021-08-24 07:56:01
【问题描述】:

我想将原始 API 响应存储在 Clob 中,并通过一个过程以通用方式解析 Clob 中包含的 json - 这意味着我必须动态构建 json 格式。这样做的一种方法是正则表达式第一个键:值对,并提取所有键中的一行作为列标题。然后可以以某种方式使用这些列标题来动态创建 json 格式,然后可以解析给定 clob 中的其余 json。

我这里有这个标准的 sn-p 用于解析已知的 json-string:

SELECT * FROM json_table((select json_response from Some_Table where Condition=#input_parameter#), '$[*]'
                 COLUMNS 
                                      column1 varchar(256) PATH '$.column1',
                                      column2 varchar(256) PATH '$.column2',
                                      column3 varchar(256) PATH '$.column3',
                   )

这里,column1、column2 和column3 是已知和定义的。假设选择了不同的 Clob,并带有其他列 - 我需要将 json 中的键值正则表达式作为列标题,然后动态创建上面看到的列定义(标准类型 varchar(256) 以保持简单)。

clob 中的 Json 是非嵌套 json 字符串的列表。 json 始终具有这种格式:

[{"column1":value1,"column2":"value2","column3":value3}, {...}, {...}, ...]

因此,正则表达式将捕获第一个 {} 对之间的所有键值,并提取分号分隔键值对左侧的所有字段的值。

从这里开始,如何从正则表达式结果动态创建列定义?

上面给定的示例数据为这个表提供了上面的查询:

<table style="width: 5%;" border="2" cellpadding="5">
  <tbody>
    <tr>
      <td>&nbsp;column1&nbsp;</td>
      <td>&nbsp;column2&nbsp;</td>
      <td>&nbsp;column3&nbsp;</td>
    </tr>
    <tr>
      <td>&nbsp;value1</td>
      <td>&nbsp;value2</td>
      <td>&nbsp;value3</td>
    </tr>
  </tbody>
</table>

给定一个包含任意数量列的 clob,我需要以类似的方式提取列并创建一个表,如下所示:

<table style="width: 5%;" border="2" cellpadding="5">
  <tbody>
    <tr>
      <td style="width: 20%;">&nbsp;column1</td>
      <td style="width: 16.2539%;">column2&nbsp;</td>
      <td style="width: 16.7461%;">column3&nbsp;</td>
      <td style="width: 17%;">...&nbsp;</td>
      <td style="width: 19%;">column_n&nbsp;</td>
    </tr>
    <tr>
      <td style="width: 20%;">&nbsp;value1</td>
      <td style="width: 16.2539%;">&nbsp;value2</td>
      <td style="width: 16.7461%;">&nbsp;value3</td>
      <td style="width: 17%;">...</td>
      <td style="width: 19%;">&nbsp;value</td>
    </tr>
    <tr>
      <td style="width: 20%;">&nbsp;...</td>
      <td style="width: 16.2539%;">&nbsp;...&nbsp;</td>
      <td style="width: 16.7461%;">&nbsp;...</td>
      <td style="width: 17%;">...&nbsp;</td>
      <td style="width: 19%;">&nbsp;...</td>
    </tr>
    <tr>
      <td style="width: 20%;">&nbsp;value</td>
      <td style="width: 16.2539%;">&nbsp;value</td>
      <td style="width: 16.7461%;">&nbsp;value</td>
      <td style="width: 17%;">...&nbsp;</td>
      <td style="width: 19%;">&nbsp;value</td>
    </tr>
  </tbody>
</table>

【问题讨论】:

    标签: oracle


    【解决方案1】:

    不要使用正则表达式解析JSON;使用适当的 JSON 解析器。

    来自this answer,您可以编写函数:

    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;
    /
    

    然后使用查询:

    SELECT t.id,
           jt.idx,
           k.COLUMN_VALUE AS Key,
           get_value( jt.json_obj, k.COLUMN_VALUE ) AS value
    FROM   TABLE_NAME t
           CROSS APPLY JSON_TABLE(
             t.json_value,
             '$[*]'
             COLUMNS (
               idx FOR ORDINALITY,
               json_obj VARCHAR2(4000) FORMAT JSON PATH '$'
             )
           ) jt
           CROSS APPLY get_keys( jt.json_obj ) k
    

    其中,对于样本数据:

    CREATE TABLE table_name (
      id         NUMBER
                 GENERATED ALWAYS AS IDENTITY
                 PRIMARY KEY,
      json_value CLOB
                 CHECK( json_value IS JSON )
    );
    
    INSERT INTO table_name ( json_value ) VALUES (
    '[{"column1":"value1","column2":"value2","column3":"value3"},
    {"column1":"value4","column2":"value5","column3":"value6"},
    {"column3":"value9","column1":"value7","column2":"value8"}]'
    )
    

    输出:

    ID IDX KEY VALUE
    1 1 column1 value1
    1 1 column2 value2
    1 1 column3 value3
    1 2 column1 value4
    1 2 column2 value5
    1 2 column3 value6
    1 3 column3 value9
    1 3 column1 value7
    1 3 column2 value8

    db小提琴here

    从这里开始,如何从 [...] 结果动态创建列定义?

    你想要一个dynamic pivot;这不是 Oracle(或大多数 RDBMS)支持的东西,您最好在用于访问数据库的任何中间层编程应用程序(Java、PHP、C#、Python 等)中将行转换为列。


    如果您知道将有 3 个键,那么您可以使用:

    SELECT *
    FROM   (
      SELECT t.id,
             jt.idx,
             k.COLUMN_VALUE AS Key,
             get_value( jt.json_obj, k.COLUMN_VALUE ) AS value,
             ROW_NUMBER() OVER (PARTITION BY t.id, jt.idx ORDER BY k.COLUMN_VALUE)
               AS key_index
      FROM   TABLE_NAME t
             CROSS APPLY JSON_TABLE(
               t.json_value,
               '$[*]'
               COLUMNS (
                 idx FOR ORDINALITY,
                 json_obj VARCHAR2(4000) FORMAT JSON PATH '$'
               )
             ) jt
             CROSS APPLY get_keys( jt.json_obj ) k
    )
    PIVOT (
      MAX(key) AS key,
      MAX(value) AS value
      FOR key_index IN (1, 2, 3)
    )
    

    哪些输出:

    ID IDX 1_KEY 1_VALUE 2_KEY 2_VALUE 3_KEY 3_VALUE
    1 1 column1 value1 column2 value2 column3 value3
    1 2 column1 value4 column2 value5 column3 value6
    1 3 column1 value7 column2 value8 column3 value9

    注意:您无法将列名动态设置为 JSON 键,并且您无法使其适用于动态数量的列。

    db小提琴here

    【讨论】:

    • 这非常接近提取我需要的内容。然而;我需要将列保持为表格形式,而不是将列转为行。使用 OP 中提供的代码,我可以将 Clob 中的 json 字符串转换为表,但我需要对作为 clob 传递的任何 json 动态执行相同的操作(并且我可能不知道列定义)。如果没有枢轴,上面的内容会如何?
    • @bullfighter 见最后一段;在 Oracle 中不可能进行动态数据透视,您应该使用中间层应用程序将行转换为列。 注意:查询中没有枢轴(或取消枢轴)。
    • @bullfighter 我为已知的固定数量的键添加了一个选项。
    • 感谢您花时间回答和解释。很遗憾,Oracle 缺少这样的功能:( 是否可能存在“hacky”变体,即通过构造查询字符串并使用 Execute Immediate 执行它?我在想象:1)使用 json-lib 检索键字符串2) 循环遍历字符串元素,使用 concat 构造列定义并将其添加到包含所有列定义的主字符串 3) 通过构造查询字符串执行立即在 OP 中执行查询的更改版本
    • 另一种选择是创建数十个个性化的存储过程,这些过程可以从源头捕获每个单独的 json 字符串格式的可能性,这听起来既危险又混乱。遗憾的是我不能使用 Python 或其他东西,因为数据是由 ETL 工具加载的(带有贬值的 json 解析),因此从 json 到表格格式的转换必须在 oracle 中完成
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-25
    相关资源
    最近更新 更多