值大于 32K 的子字符串 CLOB 值

Substring CLOB value with value more than 32K

请问,我正在尝试通过删除前 33 个字符和最后 2 个字符来对 clob 值进行子字符串处理,

我尝试使用以下简单代码,但返回错误:ORA-06502:PL/SQL:数字或值错误

DECLARE
 p_Clob_Input CLOB := ''; --> value more than 32K
 p_Clob_Output CLOB; --> input CLOB value after removing first 33 characters and last 2 characters
BEGIN
 Dbms_Lob.Createtemporary(p_Clob_Output, FALSE);
 Dbms_Lob.Writeappend(p_Clob_Output, Dbms_Lob.Getlength(p_Clob_Input)-35,Dbms_Lob.Substr(p_Clob_Input,Dbms_Lob.Getlength(p_Clob_Input)-2, 33));
END;

然后我尝试使用以下代码,它工作正常,但是,如果长度为 32001 或 64001,它仍然会失败,而且我觉得实现 objective 的代码太长了,

DECLARE
 p_Clob_Index NUMBER;
 p_Length   NUMBER;
 p_Chunk    VARCHAR2(32000);
 p_Clob_Input CLOB := ''; --> value more than 32K
 p_Clob_Output CLOB; --> input CLOB value after removing first 33 characters and last 2 characters
BEGIN
 Dbms_Lob.Createtemporary(p_Clob_Output, FALSE);
 p_Length   := Dbms_Lob.Getlength(p_Clob_Input);
 p_Clob_Index := 1;
 WHILE p_Clob_Index <= p_Length
 LOOP
  IF p_Clob_Index = 1
  THEN
   IF p_Length > 32000
   THEN
    p_Chunk := Dbms_Lob.Substr(p_Clob_Input, 32000, 33);
   ELSE
    p_Chunk := Dbms_Lob.Substr(p_Clob_Input, p_Length - 2, 33);
   END IF;
  ELSE
   IF p_Clob_Index > p_Length - 32000
   THEN
    p_Chunk := Dbms_Lob.Substr(p_Clob_Input, (p_Length - p_Clob_Index) - 1, p_Clob_Index);
   ELSE
    p_Chunk := Dbms_Lob.Substr(p_Clob_Input, 32000, p_Clob_Index);
   END IF;
  END IF;
  IF p_Clob_Index > p_Length - 32000
  THEN
   p_Clob_Index := p_Length + 1;
  ELSE
   p_Clob_Index := p_Clob_Index + 32000;
  END IF;
  Dbms_Lob.Writeappend(p_Clob_Output, Length(p_Chunk), p_Chunk);
 END LOOP;
END;

感谢您的支持

我的数据库版本是 11.2.0.4.0

谢谢...

您的第一个代码块因值较大而失败,因为 the second argument to writeappend()varchar2,因此限制为 32k(在 PL/SQL 上下文中,4k 来自 SQL) .

您可以改用 the copy procedure,它有 CLOB 参数:

DBMS_LOB.COPY (  
  dest_lob    IN OUT NOCOPY CLOB  CHARACTER SET ANY_CS,  
  src_lob     IN            CLOB  CHARACTER SET dest_lob%CHARSET,  
  amount      IN            INTEGER,  
  dest_offset IN            INTEGER := 1,  
  src_offset  IN            INTEGER := 1);  

所以你可以这样做:

dbms_lob.createtemporary(p_clob_output, false);
dbms_lob.copy(p_clob_output, p_clob_input, dbms_lob.getlength(p_clob_input) - 35, 1, 34);

db<>fiddle demo

您的变量名(以及您用“值超过 32K”暗示的赋值,这也不适用于大字面值)表明您可能正在努力执行此操作;你可以这样做:

create or replace procedure your_proc (
  p_clob_input in clob,
  p_clob_output out clob,
  p_trim_start number,
  p_trim_end number
) as
begin
  dbms_lob.createtemporary(p_clob_output, false);
  dbms_lob.copy(
    dest_lob => p_clob_output,
    src_lob => p_clob_input,
    amount => dbms_lob.getlength(p_clob_input) - (p_trim_start + p_trim_end),
    dest_offset => 1,
    src_offset => p_trim_start + 1);
end;
/

db<>fiddle

虽然这是否真的会让您的生活比直接调用 dbms_lob 更轻松,但仍有争议。

只需使用 SUBSTR,因为它适用于 CLOB 超过 32767 个字符的值:

DECLARE
 p_Clob_Input   CLOB := EMPTY_CLOB();
 p_Clob_Output  CLOB;
 p_start_offset PLS_INTEGER := 33;
 p_end_offset   PLS_INTEGER := 2;
BEGIN
 FOR i IN 1 .. 10 LOOP
   p_clob_input := p_clob_input || LPAD('1234567890', 4000, '1234567890');
 END LOOP;
 
 p_clob_output := SUBSTR(
   p_clob_input,
   p_start_offset + 1,
   LENGTH( p_clob_input ) - p_start_offset - p_end_offset
 );

 DBMS_OUTPUT.PUT_LINE( 'Lengths:' );
 DBMS_OUTPUT.PUT_LINE( LENGTH( p_clob_input ) );
 DBMS_OUTPUT.PUT_LINE( LENGTH( p_clob_output ) );
 DBMS_OUTPUT.PUT_LINE( 'Starts:' );
 DBMS_OUTPUT.PUT_LINE( SUBSTR( p_clob_input, 1, 40 ) );
 DBMS_OUTPUT.PUT_LINE( LPAD( ' ', p_start_offset, ' ' ) || SUBSTR( p_clob_output, 1, 40 - p_start_offset ) );
 DBMS_OUTPUT.PUT_LINE( 'Ends:' );
 DBMS_OUTPUT.PUT_LINE( SUBSTR( p_clob_input, 39961 ) );
 DBMS_OUTPUT.PUT_LINE( SUBSTR( p_clob_output, 39961 - p_start_offset ) );
END;
/

输出:

Lengths:
40000
39965
Starts:
1234567890123456789012345678901234567890
                                 4567890
Ends:
1234567890123456789012345678901234567890
12345678901234567890123456789012345678

db<>fiddle here