在 oracle 18 中通过 json_table 解析 json
Parse json through json_table in oracle 18
有一个请求例如:
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 |
您可以定义函数:
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 不支持动态列数,因此您需要提供固定列数,例如 key
和 value
以及将输出作为行而不是列。)
db<>fiddle here
如果您真的想要可变数量的列,那么您将需要对基于列的结果进行动态透视(例如来自@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
它还显示了一个调用函数并打印引用游标内容的虚拟匿名块(因为 db<>fiddle 不支持 select func_returning_ref_cursor from dual
,您可以在 SQL 开发人员等)如:
1444284517:4255;2187606199:ONB2B3BB8:25.04.2021
...但这证明了这种方法的一个问题:调用者必须提前知道列的数量和类型,或者它自己必须使用一些动态处理元素。
您可能还想探索更广泛的 JSON Data Guide 功能。
SQL/JSON 函数 json_table 将特定 JSON 数据投影到各种 SQL 数据类型的列。您可以使用它 将 JSON 文档 的部分映射到新的虚拟 table 的行和列中,您也可以将其视为内联视图.
因为您的目标似乎是:通过 json_table 解析 json。我建议阅读 JSON 数据指南功能。here。例如,您可以根据行的结构创建视图,DBMS_JSON 包将为您创建 SQL/JSON。无需编码。您还可以将虚拟列添加到原始 table.
使用您的文档创建 table,
我添加了一个新字段 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
有一个请求例如:
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 |
您可以定义函数:
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 不支持动态列数,因此您需要提供固定列数,例如 key
和 value
以及将输出作为行而不是列。)
db<>fiddle here
如果您真的想要可变数量的列,那么您将需要对基于列的结果进行动态透视(例如来自@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
它还显示了一个调用函数并打印引用游标内容的虚拟匿名块(因为 db<>fiddle 不支持 select func_returning_ref_cursor from dual
,您可以在 SQL 开发人员等)如:
1444284517:4255;2187606199:ONB2B3BB8:25.04.2021
...但这证明了这种方法的一个问题:调用者必须提前知道列的数量和类型,或者它自己必须使用一些动态处理元素。
您可能还想探索更广泛的 JSON Data Guide 功能。
SQL/JSON 函数 json_table 将特定 JSON 数据投影到各种 SQL 数据类型的列。您可以使用它 将 JSON 文档 的部分映射到新的虚拟 table 的行和列中,您也可以将其视为内联视图.
因为您的目标似乎是:通过 json_table 解析 json。我建议阅读 JSON 数据指南功能。here。例如,您可以根据行的结构创建视图,DBMS_JSON 包将为您创建 SQL/JSON。无需编码。您还可以将虚拟列添加到原始 table.
使用您的文档创建 table,
我添加了一个新字段 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