0

我正在尝试通过 Oracle 11g 中的 PL/SQL构建一些非常大的 XML 文件。我正在尝试迭代地构建文件 - 获取一行,写入文件,获取下一行等。下面是我的代码。我在哪里定义 CLOB 时遇到问题。

我收到两个错误,具体取决于我初始化和释放 CLOB 的位置: 1-超出内存——在循环前初始化 CLOB 和循环后的 freetemporary 2-找不到 clob(指定的 LOB 定位器无效)——在循环中初始化时和free in loop,或者初始化 out 并且 free in loop

请告知我的方法中的问题是什么,或者最好的方法是迭代地构建一个大型 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;
4

1 回答 1

1

尝试在 PL/SQL 中生成非常大的 XML 是没有意义的。问题不在于 PL/SQL 本身,而是 PL/SQL 只支持 XML DOM,而 DOM 根本不能很好地处理大型 XML。您没有说您拥有的 XML 文档的大小,但如果发现 PL/SQL 用于构建您的文档的内存大约是生成的文档大小的 10 到 30 倍,我不会感到惊讶。

有没有使用 PL/SQL 以外的东西生成 XML 的选项?如果没有,而且我真的必须在 Oracle 数据库中生成大型 XML 文件,我会考虑使用 Java 存储过程。 这个问题有一些关于如何在 Java 中做这种事情的答案。

编辑以回应您的评论:您的代码绝对不是一次写一行。它一起写了很多,我通过SELECT * FROM all_objects在我的 Oracle 11g XE 数据库上使用查询运行它来验证这一事实。循环运行一次并写入了 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 存储过程中做任何你需要做的事情,或者在数据库之外做。

于 2013-10-15T20:56:35.327 回答