Error : ORA-01704: string literal too long. Dynamically assign CLOB to variable

Error : ORA-01704: string literal too long. Dynamically assign CLOB to variable

我的问题很重要。

我应该执行运行 INSERT INTO 查询的 PL/SQL 脚本。看起来像:

DECLARE
newId NUMBER(38,0) := &1;
BEGIN
Insert into FOO ("ID", "DESCRIPTION")
values (newId+1, 'LARGE CLOB WHICH PRODUCES EXCEPTION');
-- A LOT OF INSERT QUERIES
END;
/
exit;

因此,我发现将 CLOB 分配给 VARCHAR2 变量是个好主意,因为它可能有 32767 字节长。我的目标是为每个 INSERT INTO 查询执行此操作。喜欢:

--assign CLOB to VARCHAR2 variable
-- INSERT variable instead of CLOB type

我想指出我在脚本中有很多 INSERT INTO 查询,所以我应该在每个 INSERT INTO 查询之前重新分配变量,我该怎么做?

您收到 ORA-01704 是因为您的字符串文字超过 4000 字节,这是 SQL 调用中字符串文字的大小限制。在 PL/SQL 中,限制是 32k,因此如果您的所有值都小于该值,您可以将它们分配给 PL/SQL 变量并将其用于插入:

DECLARE
  newId NUMBER(38,0) := &1;
  newDescription varchar2(32767); -- or clob
BEGIN
  newDescription := 'LARGE CLOB WHICH PRODUCES EXCEPTION';
  Insert into FOO ("ID", "DESCRIPTION")
  values (newId+1, newDescription);

  newDescription := 'ANOTHER LARGE CLOB WHICH PRODUCES EXCEPTION';
  Insert into FOO ("ID", "DESCRIPTION")
  values (newId+1, newDescription);

  ...
END;
/

如果任何值超过 32k,您将需要一个 PL/SQL CLOB 变量,并且需要通过附加短的 (<32k) 字符串文字来构造它,这很混乱。

无论如何,使用多个插入语句可能不是最好的方法。您可以使用 SQL*Loader 或外部 table 来更简单地加载数据。或者您可以使用 utl_file 读取值,例如进入同一个 PL/SQL 变量,然后插入一个循环 - 这样代码会更少,也更容易维护。

您还可以使用集合来保存字符串值:

DECLARE
  TYPE stringTab IS table of varchar2(32767); -- or clob
  newDescriptions stringTab := new stringTab();
BEGIN
  newDescriptions.extend;
  newDescriptions(newDescriptions.last) := 'LARGE CLOB WHICH PRODUCES EXCEPTION';

  newDescriptions.extend;
  newDescriptions(newDescriptions.last) := 'ANOTHER LARGE CLOB WHICH PRODUCES EXCEPTION';

  forall i in newDescriptions.first..newDescriptions.last
    insert into FOO ("ID", "DESCRIPTION")
    values (&1 + 1, newDescriptions(i));
END;
/

... 这将是性能和(可能)可读性与集合内存使用之间的权衡。如果您的情况可行,您可以将其填充到块中,或者再次从文件中将值读入集合。


您仍然可以通过对现有 table 的查询生成此信息,例如:

set pages 0
set lines 32767
set long 32767
set define off

select 'DECLARE' || chr(10)
  || '  newId NUMBER(38,0) := &1;' || chr(10)
  || '  newDescription varchar2(32767);' || chr(10)
  || 'BEGIN'
from dual;

select '  newDescription := q''[' || description || ']'';' || chr(10)
  || '  newId := newId + 1;' || chr(10)
  || '  insert into FOO ("ID", "DESCRIPTION") values (newId, newDescription);' || chr(10)
from foo;

select 'END;' || chr(10)
  || '/' || chr(10)
  || 'exit'
from dual;

set define on

我使用了 the alternative quoting mechanism 以防您的任何字符串值包含单引号,但您需要选择一个 suitable 引号分隔符。再次假设您的 CLOB 值的 none 超过 32k。

如果你真的想用一个充满插入语句的脚本来做这件事,我也会重新考虑;如果数据来自 table,那么 export/import 可能更合适。

您也可以在 PL/SQL 中声明一个 clob 变量。请参阅 lob semantics and the DBMS_LOB 包来操作它。它可以是这样的:

DECLARE
   myLOBFromDatabase CLOB;
BEGIN
   SELECT lobComun INTO myLOBFromDatabase FROM table WHERE id=1;


   /* Manipulate lob wth dbms_lob package at will here */  

    Insert into FOO ("ID", "DESCRIPTION")
    values (newId+1, myLOBFromDatabase );

END;

我对此的解决有点奇怪,但它有效。

DECLARE 
JS CLOB;
BEGIN
  JS := TO_CLOB( 'THE FIRST PART OF CLOB SHOULD BE LESS THEN 32K
                  ......
                  ......'||
                 '....... 
                  THIS IS SECOND PART IS LESS THEN 32K TOO'||
                 '....... 
                  LAST PART');
END;

这样让我插入了1M多