oracle 如何通过PLSQL逐行拆分CLOB(某些行的字符数超过32K)?

jvidinwx  于 2023-03-07  发布在  Oracle
关注(0)|答案(3)|浏览(351)

我正在尝试拆分一个包含超过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

表 * data_tab * 包含一些以管道作为分隔符的文件。列 * file_cont * 是一个clob,其中包含我们感兴趣的文件。然而,当我尝试执行上述查询时,它看起来像是一个无限循环。
仅供参考,CLOB包含600多行。
我想做的是将clob一行一行地拆分为不同的CLOB。您知道一个查询可以显示此结果而不会陷入无限循环吗?
编辑:文件大小为22MB。
先谢谢你。

jum4pzuy

jum4pzuy1#

我有一个特殊的包用于split和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;
/

更新:修复了长匹配的错误:返回varchar2的dbms_lob.substr替换为返回clob的substr(clob)。

piztneat

piztneat2#

可以使用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

是:
| 价值|长度|
| - ------|- ------|
| 啊啊啊啊啊|四万|
| BBBBBBBB|四万|
| 中国|四万|
| 日|四万|

  • db〈〉小提琴here *
4zcjmb1e

4zcjmb1e3#

您可以在纯SQL中执行此操作,首先将数据转换为xmltype,然后使用xmltable提取行:

select x.line
  from data_tab d
     , xmltable('/d/x' passing xmltype('<d><x>'||replace(dbms_xmlgen.convert(d.file_cont),chr(10),'</x><x>')||'</x></d>')
                       columns line clob path '/') x
 where d.interface = 'Historical'

相关问题