如何在假脱机 XML 结果时查询特定行数以避免缓冲区溢出

How to query a specific number of rows in order to avoid buffer overflow when spooling XML result

我有一个运行 sqlplus 命令的脚本,以便生成 XML 结果并将其假脱机到文件中。

一切顺利,除了我在大 table 上做的时候。当我得到一个大于 1000000 字节的 clob 时引发缓冲区溢出异常...

然后我正在寻找一种方法来根据该 clob 大小执行多个 SELECT 语句。 例如:如果我得到一个 4 000 000 字节的 clob,我将执行 4 select,每个都在我的 table 的 1/4 行上执行。 你知道我该怎么做吗?

这是我的代码示例:

sqlplus -s "DB" << EOF > /dev/null

set serveroutput on size unlimited;
set feedback off;
set pagesize 0;
set linesize 300;
set trimspool on;
set trimout on;
set termout off;
set wrap on;
set arraysize 200;

spool file.xml append;

DECLARE
   l_xmltype XMLTYPE;
   l_ctx dbms_xmlgen.ctxhandle;
   l_var VARCHAR(40);
   v_clob CLOB;
   v_clob_length INTEGER;
   amount INTEGER = 32767;
   buffer VARCHAR2(32767);
   pos INTEGER;

BEGIN

     l_ctx := dbms_xmlgen.newcontext('SELECT a.* FROM '||tableName||' a');

     dbms_xmlgen.setrowsettag(l_ctx, 'Table-'||tableName); 
     dbms_xmlgen.SetNullHandling(l_ctx, dbms_xmlgen.EMPTY_TAG);

     l_xmltype := dbms_xmlgen.getXmlType(l_ctx);
     dbms_xmlgen.closeContext(l_ctx);

     SELECT XMLSERIALIZE(DOCUMENT l_xmltype AS CLOB) into v_clob FROM dual;
     v_clob_length := length(v_clob);

     IF v_clob_length > 1000000 THEN 

        -- HERE (buffer overflow raised)
        return;

     END IF;

     WHILE pos < v_clob_length LOOP
        dbms_lob.read(v_clob, amount, pos, buffer);
        dbms_output.put_line(buffer);
        pos := pos + amount;
     END LOOP;

END;
/
spool off;

EOF

如果你有更好的解决办法,请告诉我! :)

非常感谢!

没关系,我找到解决办法了!

遇到这个问题的人:

我刚刚添加了一个条件来验证生成的 clob 是否大于 1 000 000 字节。如果是这样,我将该 clob 大小除以 1000000 并得到一个整数,我检索 table 的行数,然后通过在每次迭代中仅选择一部分行来循环。

代码如下:

IF v_clob_length > 1000000 THEN
   nbr_iteration := (v_clob_length/1000000)+1;
   execute immediate 'SELECT count(*) FROM '||myTable into nbr_rows;
   FOR i in 1..nbr_iteration LOOP
     current_calc := ((i/nbr_iteration)*nbr_rows)+1;
     l_ctx := dbms_xmlgen.newcontext('SELECT t.rowid, t.* FROM (SELECT t.*, row_number() over (order by rowid) rowNumber FROM '||myTable||' t) t WHERE rowNumber BETWEEN '||previous_calc||' AND '||current_calc||'');
     previous_calc := current_calc+1;
   END LOOP;
END IF;

我知道我的问题不是很清楚,但还是谢谢了! :)