【问题标题】:Substring CLOB value with value more than 32K值大于 32K 的子字符串 CLOB 值
【发布时间】:2021-05-10 06:34:25
【问题描述】:

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

我尝试使用以下简单代码,但返回错误:ORA-06502: PL/SQL: numeric or value error

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,它仍然会失败,而且我觉得代码太长,无法实现目标,

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

谢谢...

【问题讨论】:

    标签: oracle plsql substr clob


    【解决方案1】:

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

    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小提琴here

    【讨论】:

      【解决方案2】:

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

      您可以改用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 相比,这是否真的会让您的生活更轻松,这是有争议的。

      【讨论】:

        猜你喜欢
        • 2015-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-12
        • 1970-01-01
        • 1970-01-01
        • 2021-01-17
        • 2019-03-16
        相关资源
        最近更新 更多