我正在尝试通过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;
答案 0 :(得分: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中执行此类操作有一些答案。
编辑以回应您的评论:您的代码绝对不是一次只写一行。它正在编写这个批次,我通过在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 ... ?>
声明。
我支持我之前的建议。无论何时需要处理大型XML,在Oracle数据库或其他地方,都要使用SAX或StAX。 PL / SQL也不支持,所以你需要在Java存储过程中做任何事情,或者从数据库中做。