SQL 另外 - 如何将大字符串传递给过程 CLOB 的参数

SQL Plus - how to pass big string to procedure CLOB's parameter

我有接收 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吗?

谢谢

您可以尝试循环遍历输入文件,一次附加 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,它没有变量大小的限制。

您能否尝试在 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;

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

#!/bin/bash

file=$(curl -s "http://example.com/someFile.xml" | sed -r "s/(.{1,2000})/l_clob := l_clob || '';\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 || '';\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 || '';\
/g")