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

时间:2017-06-08 22:39:44

标签: oracle stored-procedures oracle11g sqlplus

我有接收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,我有什么可以做的吗?

由于

3 个答案:

答案 0 :(得分: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次重复模式(通过{{1在RE_DUMP_MAX中设置,而不是我知道在运行时可以修改的。)

您可以使用下限:

limits.h

在Linux下实际上要快得多,所以无论如何都不是一个糟糕的选择。

在对macOS进行进一步实验之后(El Cap,但对Sierra来说可能是相同的)并尝试使用转义换行符而不在输出中包含文字file=$(curl -s "http://example.com/someFile.xml" | sed -E "s/(.{1,255})/l_clob := l_clob || '\1';\n/g") n,从而导致PLS-00103 ,将实际的换行放在:

中似乎更容易
\n

答案 1 :(得分:0)

您可以尝试循环输入文件,一次附加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 :(得分:0)

你能尝试用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;