使用 APEX_JSON 生成 JSON 文档
Posted
技术标签:
【中文标题】使用 APEX_JSON 生成 JSON 文档【英文标题】:Generate JSON document using APEX_JSON 【发布时间】:2020-06-30 08:35:40 【问题描述】:让我解释一下我的情况,您可能会更好地理解我的问题。我们需要从一组具有特定格式的表中创建一个 JSON 文件。我有使用 APEX_JSON 的想法,因为它很简单,但是我遇到了内存问题,因为 json 文档会生成 500 万条记录。实际上,当我通过应用 rownum 过滤器减少记录数时,该过程有效。但是,当查询尝试创建包含记录总数的json时,我得到了一个PL-SQL数字错误,这基本上是内存溢出。
我原来的JSON查询如下:
DECLARE
l_cursor SYS_REFCURSOR;
BEGIN
for l_hdr_row in (select FILENAME, REPORT_DATE, DOMAINCODE, LEGALENTITYCODE from RDM_OUT.JSON_NKEY_REP_HDR where DOMAINCODE = '00001')
loop
APEX_JSON.INITIALIZE_CLOB_OUTPUT;
APEX_JSON.OPEN_OBJECT;
APEX_JSON.OPEN_OBJECT(l_hdr_row.FILENAME);
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('Date',l_hdr_row.REPORT_DATE);
APEX_JSON.WRITE('DomainCode',l_hdr_row.DOMAINCODE);
APEX_JSON.WRITE('LegalEntityCode',l_hdr_row.LEGALENTITYCODE);
APEX_JSON.OPEN_ARRAY('Keys');
FOR dtl IN (SELECT NATIVEKEY, MASTERKEY, ENDDATE
FROM RDM_OUT.JSON_NKEY_REP_DTL DTL
WHERE DTL.FILENAME = l_hdr_row.FILENAME) LOOP
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('NativeKey',dtl.NATIVEKEY);
APEX_JSON.WRITE('MasterKey',dtl.MASTERKEY);
APEX_JSON.WRITE('EndDate',dtl.ENDDATE);
APEX_JSON.CLOSE_OBJECT;
END LOOP;
APEX_JSON.CLOSE_ARRAY;
APEX_JSON.CLOSE_OBJECT;
APEX_JSON.CLOSE_ALL;
DBMS_OUTPUT.PUT_LINE(APEX_JSON.GET_CLOB_OUTPUT);
APEX_JSON.FREE_OUTPUT;
end loop;
END;
/
第一个循环实际上只恢复了一行,这在第二个循环中得到了处理,在那里我得到了第一个循环中唯一出现的 550 万条记录。我尝试使用 JSON_OBJECT 和 JSON_ARRAY,但我无法获得 APEX_JSON 给我的相同输出格式。
我使用的是 Oracle 12.2,因此无法获得 18c 和 19c 中某些 JSON 函数的改进。
让我向您展示我使用 APEX_JSON 处理原始查询的结果
SQL> set serveroutput on size unlimited echo on timing on
SQL> DECLARE
2 l_cursor SYS_REFCURSOR;
BEGIN
3 4
5 for l_hdr_row in (select FILENAME, REPORT_DATE, DOMAINCODE, LEGALENTITYCODE from RDM_OUT.JSON_NKEY_REP_HDR where DOMAINCODE = '00001')
loop
6 7 8 APEX_JSON.INITIALIZE_CLOB_OUTPUT;
9 APEX_JSON.OPEN_OBJECT;
10 APEX_JSON.OPEN_OBJECT(l_hdr_row.FILENAME);
11
APEX_JSON.OPEN_OBJECT;
12 13 APEX_JSON.WRITE('Date',l_hdr_row.REPORT_DATE);
APEX_JSON.WRITE('DomainCode',l_hdr_row.DOMAINCODE);
14 15 APEX_JSON.WRITE('LegalEntityCode',l_hdr_row.LEGALENTITYCODE);
APEX_JSON.OPEN_ARRAY('Keys');
16 17
FOR dtl IN (SELECT NATIVEKEY, MASTERKEY, ENDDATE
FROM RDM_OUT.JSON_NKEY_REP_DTL DTL
18 19 20 WHERE DTL.FILENAME = l_hdr_row.FILENAME and rownum < 5 ) LOOP
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('NativeKey',dtl.NATIVEKEY);
21 22 23 24 APEX_JSON.WRITE('MasterKey',dtl.MASTERKEY);
APEX_JSON.WRITE('EndDate',dtl.ENDDATE);
25 APEX_JSON.CLOSE_OBJECT;
END LOOP;
26 27 28 APEX_JSON.CLOSE_ARRAY;
APEX_JSON.CLOSE_OBJECT;
APEX_JSON.CLOSE_ALL;
29 30 31 32 DBMS_OUTPUT.PUT_LINE(APEX_JSON.GET_CLOB_OUTPUT);
APEX_JSON.FREE_OUTPUT;
end loop;
END;
33 34 35 36 /
"RTTA_00013_20190831_00001_00055_NKEY":
"Date":"2019-08-31"
,"DomainCode":"
00001"
,"LegalEntityCode":"00055"
,"Keys":[
"NativeKey":"85430299"
,"MasterKey
":"01483175470"
,"EndDate":"9999-12-31"
,
"NativeKey":"33227843"
,"MasterKey"
:"329401533000001934"
,"EndDate":"9999-12-31"
,
"NativeKey":"42565570"
,"Mast
erKey":"01500329780"
,"EndDate":"9999-12-31"
,
"NativeKey":"98536882"
,"Maste
rKey":"01502416501"
,"EndDate":"9999-12-31"
]
我的问题如下:
有没有更好的方法来做我正在用 APEX_JSON 做的事情?也许我可以以某种方式使用带有限制的 BULK COLLECT ?
如何使用 JSON_OBJECT 和 JSON_ARRAY 获得相同的结果和格式?我试过了,但我没有得到相同的输出格式。
谢谢大家!
【问题讨论】:
【参考方案1】:最后,我想出了自己的有效解决方案。我改为 BULK COLLECT,现在该过程完美运行。在这里你可以看到代码现在的样子:
DECLARE
c_limit CONSTANT PLS_INTEGER DEFAULT 50000;
CURSOR nkey_cur (v_filename in varchar2) IS SELECT * FROM RDM_OUT.JSON_NKEY_REP_DTL DTL where DTL.FILENAME = v_filename ;
TYPE tbl_Nativekey IS TABLE OF RDM_OUT.JSON_NKEY_REP_DTL%ROWTYPE INDEX BY PLS_INTEGER;
var_Nativekey tbl_Nativekey;
PROCEDURE print_clob_to_output (p_clob IN CLOB)
IS
l_offset INT := 1;
BEGIN
loop
exit when l_offset > dbms_lob.getlength(p_clob);
dbms_output.put_line( dbms_lob.substr( p_clob, 255, l_offset ) );
l_offset := l_offset + 255;
end loop;
END print_clob_to_output;
BEGIN
for l_hdr_row in (select FILENAME, REPORT_DATE, DOMAINCODE, LEGALENTITYCODE from RDM_OUT.JSON_NKEY_REP_HDR where DOMAINCODE = '00001' )
loop
APEX_JSON.INITIALIZE_CLOB_OUTPUT;
-- APEX_JSON.OPEN_OBJECT;
-- APEX_JSON.OPEN_OBJECT(l_hdr_row.FILENAME);
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('Date',l_hdr_row.REPORT_DATE);
APEX_JSON.WRITE('DomainCode',l_hdr_row.DOMAINCODE);
APEX_JSON.WRITE('LegalEntityCode',l_hdr_row.LEGALENTITYCODE);
APEX_JSON.OPEN_ARRAY('Keys');
OPEN nkey_cur(l_hdr_row.FILENAME);
LOOP
FETCH nkey_cur BULK COLLECT INTO var_Nativekey LIMIT c_limit;
EXIT WHEN var_Nativekey.COUNT = 0;
FOR i IN var_Nativekey.FIRST..var_Nativekey.LAST LOOP
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('NativeKey',var_Nativekey(i).NATIVEKEY);
APEX_JSON.WRITE('MasterKey',var_Nativekey(i).MASTERKEY);
APEX_JSON.WRITE('EndDate',var_Nativekey(i).ENDDATE);
APEX_JSON.CLOSE_OBJECT;
END LOOP;
END LOOP;
CLOSE nkey_cur;
APEX_JSON.CLOSE_ARRAY;
APEX_JSON.CLOSE_OBJECT;
APEX_JSON.CLOSE_ALL;
PRINT_CLOB_TO_OUTPUT(p_clob => APEX_JSON.GET_CLOB_OUTPUT);
APEX_JSON.FREE_OUTPUT;
end loop;
END;
/
我无法使用 SQL/JSON 函数获得相同的格式,因此我决定转换我正在使用的代码以获得 BULK COLLECT 的好处。我还修改了如何在输出中打印 clob 以避免缓冲区出现问题:
SQL> host cat JSON_00001_NKEY.sql
spool &1
DECLARE
c_limit CONSTANT PLS_INTEGER DEFAULT 10000; -- EXAMPLE of LIMIT
CURSOR nkey_cur (v_filename in varchar2) IS SELECT * FROM RDM_OUT.JSON_NKEY_REP_DTL DTL where DTL.FILENAME = v_filename ;
TYPE tbl_Nativekey IS TABLE OF RDM_OUT.JSON_NKEY_REP_DTL%ROWTYPE INDEX BY PLS_INTEGER;
var_Nativekey tbl_Nativekey;
PROCEDURE print_clob_to_output (p_clob IN CLOB)
IS
l_offset INT := 1;
BEGIN
loop
exit when l_offset > dbms_lob.getlength(p_clob);
dbms_output.put_line( dbms_lob.substr( p_clob, 255, l_offset ) );
l_offset := l_offset + 255;
end loop;
END print_clob_to_output;
BEGIN
for l_hdr_row in (select FILENAME, REPORT_DATE, DOMAINCODE, LEGALENTITYCODE from RDM_OUT.JSON_NKEY_REP_HDR where DOMAINCODE = '00001' )
loop
APEX_JSON.INITIALIZE_CLOB_OUTPUT;
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('Date',l_hdr_row.REPORT_DATE);
APEX_JSON.WRITE('DomainCode',l_hdr_row.DOMAINCODE);
APEX_JSON.WRITE('LegalEntityCode',l_hdr_row.LEGALENTITYCODE);
APEX_JSON.OPEN_ARRAY('Keys');
OPEN nkey_cur(l_hdr_row.FILENAME);
LOOP
FETCH nkey_cur BULK COLLECT INTO var_Nativekey LIMIT c_limit;
EXIT WHEN var_Nativekey.COUNT = 0;
FOR i IN var_Nativekey.FIRST..var_Nativekey.LAST LOOP
APEX_JSON.OPEN_OBJECT;
APEX_JSON.WRITE('NativeKey',var_Nativekey(i).NATIVEKEY);
APEX_JSON.WRITE('MasterKey',var_Nativekey(i).MASTERKEY);
APEX_JSON.WRITE('EndDate',var_Nativekey(i).ENDDATE);
APEX_JSON.CLOSE_OBJECT;
END LOOP;
END LOOP;
CLOSE nkey_cur;
APEX_JSON.CLOSE_ARRAY;
APEX_JSON.CLOSE_OBJECT;
APEX_JSON.CLOSE_ALL;
PRINT_CLOB_TO_OUTPUT(p_clob => APEX_JSON.GET_CLOB_OUTPUT);
APEX_JSON.FREE_OUTPUT;
end loop;
END;
/
spool off
exit
SQL> set serveroutput off
SQL> set timing on
SQL> @JSON_00001_NKEY.sql test.json
PL/SQL procedure successfully completed.
Elapsed: 00:02:33.89
我想肯定有一个使用 SQL/JSON 的选项,但我真的相信 APEX_JSON 提供了一个非常简单的 API。要小心内存溢出或性能问题,BULK COLLECT 可能是一个不错的方法。
【讨论】:
将 LIMIT 添加到您的 BULK COLLECT 中会更好。只是为那些没有经常使用 BULK COLLECT 的人指出。以上是关于使用 APEX_JSON 生成 JSON 文档的主要内容,如果未能解决你的问题,请参考以下文章
如何在嵌套数组中使用 apex_json.get_count
在 PL SQL 中解析 JSON 数组 APEX_JSON