在 pl/sql 脚本中处理非常大的字符串

Handle a very large string in pl/sql script

我正在尝试 运行 下面的代码,该代码读取 table A 的索引定义,以便在我 delete/create 之后可以在该脚本中再次创建它。当返回值 (ddl) 较小时,此脚本 运行 没问题,但在值较大且一行中有 140K 个字符的其他环境中,此脚本失败并出现以下错误。请注意,由于某些限制,我不能在这种情况下使用线轴。有人可以帮助解决这个问题或建议其他方法吗?

提前致谢。

"算术、数字、字符串、转换或约束错误 发生了。例如,如果尝试 将值 NULL 分配给声明为 NOT NULL 的变量,或者如果 尝试将大于 99 的整数分配给变量 声明 NUMBER(2)."

SET SERVEROUTPUT ON;
DECLARE 
my_cursor SYS_REFCURSOR;
TYPE clob_array IS VARRAY(15) OF CLOB;
index_array clob_array := clob_array();
v_clob CLOB;
--index_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
BEGIN
OPEN my_cursor FOR 'select replace(dbms_metadata.get_ddl (''INDEX'', index_name), ''"C",'', '''')
              from user_indexes
             where table_name = ''A''';

LOOP FETCH my_cursor INTO v_clob;
EXIT WHEN my_cursor%NOTFOUND;
index_array.extend;
index_array(index_array.count) := v_clob;
dbms_output.put_line(index_array(index_array.count));
END LOOP;
CLOSE my_cursor;
END;
/

我模拟了这个问题,你得到这个错误是因为 dbms_output.put_line 在服务器端显示 output.Try 切换到 UTL_FILE 或者尝试任何 alternatives

顺带一提,代码可以简化为:

declare
    type         clob_array is table of clob;
    index_array  clob_array := clob_array();
begin
    for r in (
        select replace(dbms_metadata.get_ddl('INDEX', index_name), '"C",') as index_ddl
        from   user_indexes
        where  table_name = 'A'
    )
    loop
        index_array.extend;
        index_array(index_array.count) := r.index_ddl;
        dbms_output.put_line(substr(index_array(index_array.count), 1, 32767));
    end loop;
end;

我使用 substr() 将传递给 dbms_output.put_line 的值限制为它的 documented limit。您可以通过将文本拆分成更小的块来解决它,并且可能找到位置 32767 之前最后一个空白 space 的位置以避免拆分单词。

这是我想出的:

declare
    type         clob_array is table of clob;
    index_array  clob_array := clob_array();

    procedure put_line
        ( p_text clob )
    is
        max_len constant simple_integer := 32767;
        line varchar2(max_len);
        remainder clob := p_text;
    begin
        while dbms_lob.getlength(remainder) > max_len loop
            line := dbms_lob.substr(remainder,max_len);
            line := substr(line, 1, instr(line, ' ', -1));
            remainder := substr(remainder, length(line) +1);
            dbms_output.put_line(line);
        end loop;
        
        if length(trim(remainder)) > 0 then
            dbms_output.put_line(remainder);
        end if;
    end put_line;
begin
     for r in (
        select replace(dbms_metadata.get_ddl('INDEX', index_name), '"C",') as index_ddl
        from   user_indexes
        where  table_name = 'A'
    )
    loop
        index_array.extend;
        index_array(index_array.count) := r.index_ddl;
        put_line(index_array(index_array.count));
    end loop;
end;