我们如何通过 PLSQL 逐行拆分 CLOB(有些行超过 32K 个字符)?

How do we split a CLOB (with some lines with more than 32K characters) line by line via PLSQL?

我正在尝试拆分包含超过 32K 个字符的行的巨大 CLOB。

我试过用这个

SELECT REGEXP_SUBSTR(file_cont, '[^'||chr(10)||']+', 1, LEVEL) AS substr
from data_tab where interface = 'Historical'
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(file_cont, '[^'||chr(10)||']+')) + 1

table data_tab 包含一些以管道作为分隔符的文件。 file_cont 列是一个 clob,其中包含我们感兴趣的文件。 但是,当我尝试执行上面的查询时,它看起来像是一个无限循环。

供参考,CLOB 包含 600 多行。

我想要做的是将 clob 逐行拆分为不同的 CLOB。 您知道可以显示此结果而不会陷入死循环的查询吗?

编辑:文件大小为 22MB。

提前致谢。

您可以使用 PL/SQL 函数来读取和拆分值:

如果你有数据类型:

CREATE TYPE clob_table AS TABLE OF CLOB;

然后函数:

CREATE FUNCTION split_clob(
  p_value     IN CLOB,
  p_delimiter IN VARCHAR2 DEFAULT ','
) RETURN clob_table PIPELINED
IS
  v_start  PLS_INTEGER;
  v_next   PLS_INTEGER;
  v_len    PLS_INTEGER;
BEGIN
  v_start := 1;
  LOOP
    v_next := DBMS_LOB.INSTR( p_value, p_delimiter, v_start );
    v_len  := CASE v_next WHEN 0 THEN LENGTH( p_value ) + 1 ELSE v_next END - v_start;
    PIPE ROW ( SUBSTR( p_value, v_start, v_len ) );
    EXIT WHEN v_next = 0;
    v_start := v_next + LENGTH(p_delimiter);
  END LOOP;
END;
/

示例数据:

CREATE TABLE table_name ( value CLOB );

DECLARE
  v_value TABLE_NAME.VALUE%TYPE := EMPTY_CLOB();
BEGIN
  FOR ch IN 65 .. 68 LOOP
    FOR i IN 1 .. 10 LOOP
      v_value := v_value || RPAD( CHR(ch), 4000, CHR(ch) );
    END LOOP;
    IF ch < 68 THEN
      v_value := v_value || CHR(10);
    END IF;
  END LOOP;
  INSERT INTO table_name ( value ) VALUES ( v_value );
END;
/

然后输出:

SELECT SUBSTR( s.column_value, 1, 10 ) AS value,
       LENGTH( s.column_value ) AS len
FROM   table_name t
       CROSS APPLY TABLE( split_clob( t.value, CHR(10) ) ) s

是:

VALUE LEN
AAAAAAAAAA 40000
BBBBBBBBBB 40000
CCCCCCCCCC 40000
DDDDDDDDDD 40000

db<>fiddle here

我有一个用于拆分和 PCRE 正则表达式的特殊包: https://github.com/xtender/XT_REGEXP

你可以在https://github.com/xtender/XT_REGEXP/blob/master/xt_regexp.pck

中找到这个函数
/**
 * Clob simple split
 */
  function clob_split_simple(p_clob in clob,p_delim in varchar2) 
  return clob_table pipelined is
    row clob;
    l_b number:=1;
    l_e number:=1;
    $IF DBMS_DB_VERSION.ver_le_11 $THEN
    $ELSE
    pragma UDF;
    $END
  begin
      while l_e>0
        loop
          l_e:=instr(p_clob,p_delim,l_b);
          pipe row(substr(p_clob,l_b,case when l_e>0 then l_e-l_b else length(p_clob)+length(p_delim)-l_b end));
          l_b:=l_e+length(p_delim);
        end loop;
  end clob_split_simple;

所以你可以使用这个流水线函数:

select * 
from table(xt_regexp.clob_split_simple(:clob,chr(10));

或者以这段代码为例

clob_table 只是一个 table of clob:

https://github.com/xtender/XT_REGEXP/blob/master/types.sql

create or replace type clob_table as table of clob;
/
create or replace type date_table as table of date;
/
create or replace type number_table as table of number;
/
create or replace type varchar2_table as table of varchar2(4000);
/
create or replace type xml_table as table of xmltype;
/

更新:修复了长匹配的错误:dbms_lob.substr which returns varchar2, replaced with substr(clob) which return clob.