【问题标题】:SQL Plus - how to pass big string to procedure CLOB's parameterSQL Plus - 如何将大字符串传递给过程 CLOB 的参数
【发布时间】:2017-11-10 19:31:42
【问题描述】:

我有一个接收 XML 的程序:

CREATE OR REPLACE PROCEDURE PROCESS_XML(xml IN CLOB) AS
BEGIN
  DBMS_OUTPUT.PUT_LINE('XML processing started');
END;

现在我正在编写一个 bash 脚本,该脚本将从服务器下载一些 XML 文件,对于每个文件,我将使用 SQL Plus 调用上述过程。

#!/bin/bash
file=$(curl -s "http://example.com/someFile.xml");
sqlplus myuser/mypass@myhost:1521/myscheme <<< "EXECUTE PROCESS_XML('$file')";

它适用于小文件,但对于大文件,我收到以下错误:

SQL*Plus: Release 12.1.0.2.0 Production on Thu Jun 8 18:28:19 2017
Copyright (c) 1982, 2016, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP
and Data Mining options
SQL> Input truncated to 7499 characters
SP2-0027: Input is too long (> 2499 characters) - line ignored

我有什么办法可以发送那些大的 XML 吗?

谢谢

【问题讨论】:

  • 您是否可以将文件放在可以从数据库中读取的位置,使用utl_file 或通过Oracle 目录对象作为外部表)?或者甚至从数据库内部(需要 ACL 等)从远程网站获取文件?
  • @AlexPoole 是的,这是我的第一种方法,但我没有,也不允许我更改 ACL 权限。所以如果可能的话,我想通过 SQL Plus 来做到这一点......谢谢

标签: oracle stored-procedures oracle11g sqlplus


【解决方案1】:

您可以尝试遍历输入文件,一次追加 2.4k 块,可能:

variable l_var clob;
exec :l_var := '';

-- loop here 
exec :l_var := :l_var || '$chunk';
---

exec process_xml(:l_var);

除了使用 shell 脚本,您还可以在 Java 中形成 clob,例如,逐行读取 XML,它没有变量大小的限制。

【讨论】:

    【解决方案2】:

    您可以尝试在 bash 中压缩文件数据,然后使用 utl_compress 在 PLSQL 中解压缩吗?

    类似:

    #!/bin/bash
    file=$(curl -s "http://example.com/someFile.xml" | gzip -f);
    sqlplus myuser/mypass@myhost:1521/myscheme <<< "EXECUTE PROCESS_XML('$file')";
    

    在plsql中:

    CREATE OR REPLACE PROCEDURE PROCESS_XML(xml IN CLOB) AS
       uncomp CLOB;
    BEGIN
      UTL_COMPRESS.lz_uncompress(src => xml, dst => uncomp);
      DBMS_OUTPUT.PUT_LINE('XML processing started');
    END;
    

    【讨论】:

    • 显然lz_uncompress 只接受BLOB 类型。所以我尝试了BLOB,但我收到了这些错误:SQL&gt; SP2-0552: Bind variable "?G" not declared. SQL&gt; SP2-0734: unknown command beginning "?? Z?..." - rest of line ignored.
    • 哦。试试这个 - 作为 CLOB 传入,然后转换为 BLOB,然后解压缩。 stackoverflow.com/questions/40526132/…
    【解决方案3】:

    您可以将文件内容拆分为 SQL*Plus 可接受的块,然后在匿名 PL/SQL 块中重新组合它们;这也将允许比字符串文字更长的值。例如:

    #!/bin/bash
    
    file=$(curl -s "http://example.com/someFile.xml" | sed -r "s/(.{1,2000})/l_clob := l_clob || '\1';\n/g")
    
    sqlplus -s -l myuser/mypass@myhost:1521/myscheme <<!EOF
    set serveroutput on
    declare
     l_clob clob := '';
    begin
    ${file}
     PROCESS_XML(l_clob);
    end;
    /
    exit
    !EOF
    

    EXECUTE 无论如何都是一个简单匿名块的包装器,因此使用heredoc 而不是herestring 可以让您扩展它以做更多事情。该块声明一个空的 CLOB,然后从文件中附加块 - 每个块都转换为如下所示:

     l_clob := l_clob || '<up to 2000 chars>';
    

    当 SQL*Plus 看到它时,构造的 heredoc 最终结果为:

    set serveroutput on
    declare
     l_clob clob := '';
    begin
     l_clob := l_clob || '<first 2000 chars>';
     l_clob := l_clob || '<next 2000 chars>';
     l_clob := l_clob || '<next 2000 chars>';
     ...
     l_clob := l_clob || '<last 2000 chars>';
     PROCESS_XML(l_clob);
    end;
    /
    exit
    

    稍微修改你的程序,部分是为了验证传入的长度,部分是为了检查XML在这个过程中没有被破坏:

    CREATE OR REPLACE PROCEDURE PROCESS_XML(xml IN CLOB) AS
    BEGIN
      DBMS_OUTPUT.PUT_LINE('XML processing started; CLOB length: '
        || length(xml));
      DBMS_OUTPUT.PUT_LINE('XML processing started; converted XML length: '
        || length(xmltype(xml).getclobval()));
    END;
    /
    

    使用该脚本处理大文件会产生输出:

    XML processing started; CLOB length: 368104
    XML processing started; converted XML length: 368104
    
    PL/SQL procedure successfully completed.
    

    当然,这会减慢速度;大约 360k 的文件在我的系统上花费了大约 13 秒。可能有比sed 更快的机制,但原理仍然适用。


    macOS 上的 sed 版本(需要 -E 而不是 GNU 的 -r 标志)似乎被限制为 255 次重复模式(通过 RE_DUMP_MAX,设置在 limits.h 中而不是那么远据我所知,可以在运行时修改)。

    你可以只使用一个下限:

    file=$(curl -s "http://example.com/someFile.xml" | sed -E "s/(.{1,255})/l_clob := l_clob || '\1';\n/g")
    

    这实际上在 Linux 下也快得多,所以无论如何都不是一个坏选择。


    在 macOS(El Cap,但可能与 Sierra 相同)上进行了进一步实验并尝试在不包含文字 n\n 的输出中使用转义换行符,这会导致 PLS-00103,这似乎更容易放入一个实际的换行符:

    file=$(curl -s "http://example.com/someFile.xml" | sed -E "s/(.{1,255})/ l_clob := l_clob || '\1';\
    /g")
    

    【讨论】:

    • 谢谢亚历克斯!这是一个很好的方法。唯一的问题是,对于 sed 命令,当 XML 很小时,我会得到 RE error: invalid repetition count(s)……我们能做些什么吗?
    • @qxlab - 我没有看到,即使是一个小文件。那么,使用的是哪个操作系统和版本,以及哪个 bash 版本? (我在 OEL/RedHat 5u6、bash 3.2.25 上这样做......)。您是否有导致此问题的小文件示例?
    • 这可能是问题,为了测试,我使用的是 MacOS Sierra,bash 版本是GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)。我可以使用类似 "3" 的字符串轻松重现
    • 好吧,我猜你已经把 -r 改为 -E,因为那不是 GNU sed。重复次数也有 255 个限制,所以将 2000 更改为 255。(有趣的是,这在 GNU sed 中实际上要快得多......)
    • 谢谢亚历克斯,你是对的!还有一件事,我收到错误PLS-00103: Encountered the symbol "PROCESS_XML" when expecting one of the following: := . ( @ % ;。知道那可能是什么吗?可能是另一个 bash 差异.. 谢谢
    猜你喜欢
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 2020-12-25
    • 2014-04-16
    • 2013-12-03
    • 2011-06-29
    • 1970-01-01
    相关资源
    最近更新 更多