使用 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 Apex_JSON - 解析元素名称

在 PL SQL 中解析 JSON 数组 APEX_JSON

从 JSON 文档生成 AVRO 模式

如何使用 Java 手动展平 Elasticsearch 嵌套的 JSON 文档?

在iOS中使用NSString参数迭代NSMutableArray对象后,需要生成一个JSON文档上传到服务器[关闭]