生成 XML PLSQL 迭代
Posted
技术标签:
【中文标题】生成 XML PLSQL 迭代【英文标题】:Generating XML PLSQL Iterative 【发布时间】:2013-10-15 19:04:01 【问题描述】:我正在尝试通过 Oracle 11g 中的 PL/SQL 构建一些 非常 大型 XML 文件。我正在尝试迭代地构建文件 - 获取一行,写入文件,获取下一行等。下面是我的代码。我在哪里定义 CLOB 时遇到问题。
根据我初始化和释放 CLOB 的位置,我得到两个错误: 1-超出内存——在循环前初始化 CLOB 和循环后 freetemporary 时 2- 找不到 clob(指定的 LOB 定位器无效) -- 在循环中初始化并在循环中释放时,或在循环中初始化并释放时
请告知我的方法中存在什么问题,或者最好的方法是迭代构建大型 XML 文件。
PROCEDURE sql_to_xml(p_sql IN VARCHAR2,
p_fileName IN VARCHAR2,
p_dir IN VARCHAR2,
p_xml_created OUT VARCHAR2) IS
xml_result CLOB;
doc dbms_xmldom.DOMDocument;
ctx DBMS_XMLGEN.ctxHandle;
vv_exit_code varchar2(5);
vv_ctx_open varchar2(1) := 'N';
max_rows NUMBER := 5;
BEGIN
vv_exit_code := 'XML1';
ctx := dbms_xmlgen.newcontext(p_sql);
vv_ctx_open := 'Y';
DBMS_OUTPUT.put_line(vv_exit_code);
vv_exit_code := 'XML2';
DBMS_XMLGEN.SETCONVERTSPECIALCHARS (ctx,TRUE);
DBMS_OUTPUT.put_line(vv_exit_code);
DBMS_LOB.CREATETEMPORARY(xml_result,true);
while DBMS_XMLGEN.GETNUMROWSPROCESSED(ctx) < max_rows
LOOP
vv_exit_code := 'XML3';
xml_result := dbms_xmlgen.getXML(ctx);
DBMS_OUTPUT.put_line(vv_exit_code);
DBMS_output.put_line('Xml result is: ' ||dbms_lob.substr( xml_result, 4000, 1 ));
IF xml_result is not null THEN
vv_exit_code := 'XML4';
doc := dbms_xmldom.newDOMDocument(xml_result);
DBMS_OUTPUT.put_line(vv_exit_code);
vv_exit_code := 'XML5';
dbms_xmldom.writeToFile(doc,p_dir||'/'||p_fileName, 'ISO-8859-1');
DBMS_OUTPUT.put_line(vv_exit_code);
vv_exit_code := 'XML6';
dbms_xmldom.freeDocument(doc);
p_xml_created := 'TRUE';
DBMS_OUTPUT.put_line(vv_exit_code);
ELSE
p_xml_created := 'FALSE';
END IF;
DBMS_OUTPUT.PUT_LINE('XML Result: '||xml_result);
dbms_lob.FREETEMPORARY(xml_result);
end loop;
DBMS_XMLGEN.CLOSECONTEXT (ctx);
vv_ctx_open := 'N';
EXCEPTION
WHEN out_of_process_memory THEN
IF vv_ctx_open = 'Y' THEN
DBMS_XMLGEN.CLOSECONTEXT (ctx);
END IF;
gv_err_msg := substr(sqlerrm,1,2000);
DBMS_OUTPUT.put_line(gv_process_name||' failed '||gv_err_msg);
RAISE_APPLICATION_ERROR(-20906,gv_process_name||' failed'||gv_err_msg);
dbms_output.put_line('XML_EXPORT failed (out_of_process_memory exception) executing '||p_sql);
raise_application_error(-20906,'XML_EXPORT (out_of_process_memory exception) failed executing '||p_sql);
WHEN OTHERS THEN
IF vv_ctx_open = 'Y' THEN
DBMS_XMLGEN.CLOSECONTEXT (ctx);
END IF;
if xml_result is NULL then
gv_err_msg := substr(sqlerrm,1,2000);
DBMS_OUTPUT.put_line(gv_process_name||' failed '||gv_err_msg);
-- RAISE_APPLICATION_ERROR(-20906,gv_process_name||' failed'||gv_err_msg);
dbms_output.put_line('XML_EXPORT failed (xml results are NULL) executing '||p_sql);
raise_application_error(-20906,'XML_EXPORT (xml results are NULL) failed executing '||p_sql);
else
gv_err_msg := substr(sqlerrm,1,2000);
DBMS_OUTPUT.put_line(gv_process_name||' failed '||gv_err_msg);
dbms_output.put_line('XML_EXPORT failed (others exception) executing '||p_sql);
DBMS_OUTPUT.put_line('Export Directory is: '||p_dir||'/'||p_fileName);
raise_application_error(-20906,'XML_EXPORT (others exception) failed executing '||p_sql);
end if;
END sql_to_xml;
【问题讨论】:
【参考方案1】:尝试在 PL/SQL 中生成非常大的 XML 是没有意义的。问题不在于 PL/SQL 本身,而是 PL/SQL 只支持 XML DOM,而 DOM 根本不能很好地处理大型 XML。您没有说您拥有的 XML 文档的大小,但如果发现 PL/SQL 用于构建您的文档的内存大约是生成的文档大小的 10 到 30 倍,我不会感到惊讶。
有没有使用 PL/SQL 以外的东西生成 XML 的选项?如果没有,而且我真的必须在 Oracle 数据库中生成大型 XML 文件,我会考虑使用 Java 存储过程。 This question 有一些关于如何在 Java 中做这种事情的答案。
EDIT 回应您的评论:您的代码绝对不是一次写一行。它一起写了很多,我通过在我的 Oracle 11g XE 数据库上使用查询 SELECT * FROM all_objects
运行它来验证这一事实。循环运行一次并写入了 7341 个对象,创建了一个大小刚刚超过 3MB 的 XML 文件。
然后我尝试修改您的代码以更好地支持您描述的“增量”方法。这涉及到:
添加一行 dbms_xmlgen.setmaxrows(ctx, max_rows);
来告诉 DBMS_XMLGEN 一次只生成 5 行。否则,它会尝试一次性生成批次。
将WHILE
循环顶部的代码修改为
xml_result := dbms_xmlgen.getXML(ctx);
num_rows_processed := DBMS_XMLGEN.GETNUMROWSPROCESSED(ctx);
dbms_output.put_line('Got ' || num_rows_processed || ' rows processed');
while num_rows_processed > 0
-- rest of loop omitted
在WHILE
循环底部之前添加这三行中的第一行。
然后我重新运行您的代码,我可以看到它每次都将每批五行写入文件。但是,这种方法存在一个小问题,因为每次都会覆盖文件。最后,我在输出 XML 文件中只有一条记录。我无法想象这会是你想要的。
DBMS_XMLDOM 中的 WRITETOCLOB
、WRITETOBUFFER
和 WRITETOFILE
方法并没有暗示能够附加到现有文件,老实说,我并不惊讶它们没有。如果可以,您最终会得到无效的 XML,因为文件中会有多个 <?xml ... ?>
声明。
我坚持我之前的建议。每当您需要在 Oracle 数据库或其他地方处理大型 XML 时,请使用 SAX 或 StAX。 PL/SQL 也不支持,所以在 Java 存储过程中做任何你需要做的事情,或者在数据库之外做。
【讨论】:
我的进程正在中断具有近 22k 行和大约 52 列的文件。这是在一个月的跨度内,受其创建日期的限制。列数不可协商。我知道对于“较小”并且在同一张表上工作的文件,压缩版本 (gzip) 大约是半 GB。我确实认为内存是问题,这就是我希望像上面那样构建它的原因,如果我把我的逻辑更多地列出来可能会有所帮助 - 构建 CLOB 一次加载 CLOB 一行将 CLOB 写入文件 空闲 CLOB 内存我运行的假设是,这将允许我在我去的时候释放内存 抱歉耽搁了这么久。谢谢你。由于 xml 的大小问题,我们最终改变了方法。以上是关于生成 XML PLSQL 迭代的主要内容,如果未能解决你的问题,请参考以下文章