0

我的程序

CREATE OR REPLACE PROCEDURE my_procedure(res OUT SYS_REFCURSOR , p_LstKH CLOB)
AS
CURSOR c_dsKH
  IS
    SELECT TO_NUMBER(REGEXP_SUBSTR(p_LstKH,'[^,]+', 1, level)) value FROM dual
    CONNECT BY REGEXP_SUBSTR(p_LstKH, '[^,]+', 1, level) IS NOT NULL;
BEGIN
...
END;

我想拆分长字符串 p_LstKH,然后放入 CURSOR c_dsKH。示例:p_LstKH = '1,2,....,10000'

c_dsKH.value
1
2
...
10000

但是,当我执行该过程时,我收到错误“没有足够的内存用于通过操作连接”。我尝试用 p_LstKH VARCHAR2 替换参数 p_LstKH CLOB,然后我收到其他错误“ORA-06502: PL/SQL: numeric or value error: string buffer too small”。

我现在该怎么办 ?简单地说,我想拆分一个长字符串。谢谢大家!

4

2 回答 2

2

您可以根据逗号进行自己的拆分,并使用流水线函数返回值:

create or replace function split_clob(p_lstkh clob)
return sys.odcivarchar2list pipelined
as
  start_pos pls_integer := 0;
  end_pos pls_integer := 0;
  clob_length pls_integer;
  str varchar2(4000);
begin
  clob_length := dbms_lob.getlength(p_lstkh);
  while end_pos <= clob_length loop
    start_pos := end_pos + 1;
    end_pos := dbms_lob.instr(p_lstkh, ',', start_pos, 1);
    if end_pos <= 0 then
      end_pos := clob_length + 1;
    end if;
    str := dbms_lob.substr(p_lstkh, end_pos - start_pos, start_pos);
    pipe row (str);
  end loop;
end;
/

然后您可以将其视为表格:

select * from table(split_clob('X,Y,Z'));

COLUMN_VALUE
------------
X            
Y            
Z            

如果您仍然想要作为参考游标,您的过程可以使用该选择作为游标,而不是连接方式。

作为一个演示,展示它与超过 32k 的实际(合成)clob 一起工作:

declare
  clob_val clob := 'A,B,C,D,E,F,G,H,I';
begin
  for i in 1..2000 loop
    dbms_lob.append(clob_val, ',A,B,C,D,E,F,G,H,I');
  end loop;
  dbms_output.put_line('Clob size: ' || dbms_lob.getlength(clob_val));
  for r in (select * from table(split_clob(clob_val)) where rownum < 6) loop
    dbms_output.put_line(r.column_value);
  end loop;
end;
/

anonymous block completed
Clob size: 36017
A
B
C
D
E
于 2014-03-05T12:36:02.703 回答
1

这应该可以工作,但它需要一个返回 refcursor 的函数。如果你可以使用它:

create or replace
function splitter(pc CLOB) return sys_refcursor
as
  lc sys_refcursor;
begin
  open lc for
    with s(tot, sub, offset) as (
      select pc||',' tot, 
        dbms_lob.substr(pc, dbms_lob.instr(pc, ',', 1)-1, 1) sub, 
        dbms_lob.instr(pc, ',', 1)+1 offset
      from dual
      union all
      select tot, 
        dbms_lob.substr(tot, dbms_lob.instr(tot, ',', offset+1)-offset, offset), 
        dbms_lob.instr(tot, ',', offset+1)+1
      from s
      where offset < dbms_lob.getlength(tot)
    )
    select sub
    from s;

  return lc;
end;
/

var c refcursor;
exec :c := splitter('a,b,c,d,e,f');
print c;
于 2014-03-05T16:15:43.167 回答