解析 PL/SQL 中的 JSON 字段的最有效方法是什么?

What is the most efficient way to parse JSON fields in PL/SQL?

想象一下 table my_table 的行数:

COLUMN_1 COLUMN_2 COLUMN_3
"test_1212" {date: 1646240118, name: "John", age: null} "test_2311"
"test_998" null "test_26351"
"test_56551" {age: 20} "test_3323"

此处COLUMN_2JSONnull。 JSON 字段可以有值,可以是 null,也可以不存在。

我正在编写一个查询来解压 JSON 字段 如果它存在并且有一个值 。下面是我所拥有的,我很好奇是否有多个 case 语句会影响性能,是否有更好的方法来做到这一点?

请注意,以上只是一个示例,实际 JSON 最多可以有 10 个字段。如果可以提高性能,我可以选择在代码端解压 JSON 而不是 SQL。

SELECT COLUMN_1,
       CASE WHEN json_value(COLUMN_2, '$.date') IS NOT NULL THEN json_value(COLUMN_2, '$.date')
           END AS date,
       CASE WHEN json_value(COLUMN_2, '$.name') IS NOT NULL THEN json_value(COLUMN_2, '$.name')
           END AS name,
       CASE WHEN json_value(COLUMN_2, '$.age') IS NOT NULL THEN json_value(COLUMN_2, '$.age')
           END AS age,
       COLUMN_3
FROM my_table

我正在使用 Oracle 版本 12.2.0.1.0 (12c)。

可以使用JSON_TABLE解析字段值-

SQL> with data_cte (col1, col2, col3) as
  2  (
  3  select 'COLUMN_1',json_object('date' value 1646240118, 'name' value 'John', 'age' value null), 'COLUMN_3' from dual union all
  4  select 'COLUMN_1', null, 'COLUMN_3'  from dual union all
  5  select 'COLUMN_1', json_object('date' value null, 'name' value 'Tom', 'age' value 20), 'COLUMN_3' from dual
  6  )select col1, t.*, col3 from data_cte, json_table(col2,'$'
  7  columns (
  8  date_1 varchar2(10) path '$.date',
  9  name varchar2(21) path '$.name',
 10  age varchar2(10) path '$.age')
 11  ) t;

COL1     DATE_1       NAME       AGE        COL3
-------- ------------ ---------- ---------- --------
COLUMN_1 1646240118   John                  COLUMN_3
COLUMN_1              Tom        20         COLUMN_3

您可以将 JSON_TABLEOUTER APPLY 一起使用:

SELECT column_1,
       column_3,
       j.*
FROM   my_table m
       OUTER APPLY JSON_TABLE(
         m.column_2,
         '$'
         COLUMNS (
           dt   NUMBER       PATH '$.date',
           name VARCHAR2(50) PATH '$.name',
           age  NUMBER       PATH '$.age'
         )
       ) j;

或者,如果您更喜欢使用 ANSI 标准 LATERAL 连接而不是专有的 OUTER APPLY 那么:

SELECT column_1,
       column_3,
       j.*
FROM   my_table m
       LEFT OUTER JOIN LATERAL (
         SELECT *
         FROM   JSON_TABLE(
                  m.column_2,
                  '$'
                  COLUMNS (
                    dt   NUMBER       PATH '$.date',
                    name VARCHAR2(50) PATH '$.name',
                    age  NUMBER       PATH '$.age'
                  )
                )
       ) j
       ON (1 = 1);

其中,对于示例数据:

CREATE TABLE my_table (COLUMN_1, COLUMN_2, COLUMN_3) AS
SELECT 'test_1212',  '{date: 1646240118, name: "John", age: null}', 'test_2311' FROM DUAL UNION ALL
SELECT 'test_998',   null, 'test_26351' FROM DUAL UNION ALL
SELECT 'test_56551', '{age: 20}', 'test_3323' FROM DUAL;

双输出:

COLUMN_1 COLUMN_3 DT NAME AGE
test_1212 test_2311 1646240118 John null
test_998 test_26351 null null null
test_56551 test_3323 null null 20

db<>fiddle here