我们如何通过 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.
我正在尝试拆分包含超过 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.