我在CLOB类型的Oracle数据库中有字段。我想将此字段拆分为多个列和行。以下是内容示例:
4:true5:false24:<p>option sample 1.</p>4:true22:<p>option sample 2.</p>5:false23:<p>option sample 3.</p>5:false22:<p>option sample 4.</p>5:false
结果应如下所示:
ID LEVEL ANSWER_OPTION VALUE 1 3 option sample 3 false 1 4 option sample 4 false 2 3 option sample 3 false 4 3 option sample 3 true 3 2 option sample 2 false 1 2 option sample 2 true 2 1 option sample 1 true 2 4 option sample 4 false 4 1 option sample 1 false 2 2 option sample 2 false 4 2 option sample 2 false 1 1 option sample 1 false 3 4 option sample 4 false 4 4 option sample 4 false 3 3 option sample 3 false 3 1 option sample 1 true
我们制作了以下声明,该声明创建了上述结果。
with guest_string as
( select qsn.id id
, dbms_lob.substr( qsn.guest, 2000, 1 ) answer_options
from mneme_question qsn
where qsn.id < 10
)
select distinct id
, level
, substr(regexp_substr( answer_options'<p>[^<]+', 1, level, 'i'), 4) ANSWER_OPTION
, substr(regexp_substr( answer_options, '(true|false)', regexp_instr( answer_options, '</p>', 1, 1), level, 'i'), 1) VALUE
from guest_string
connect by regexp_substr( answer_options, '<p>[^<]+', 1, level, 'i') is not null
这段代码的问题是需要多长时间来拆分我们拥有的所有记录。我们不得不将其切断为10行(5行需要0.25秒,10行需要16秒,15行需要大约2.5分钟)。我们目前有30000行,它们将会增长。目前我们无法更改软件以更改数据模型,因此我们必须临时执行此操作。
我们当前的方法是创建一个将为每条记录调用的过程,但最好是更快地解析。有没有人建议如何创建一个可以在合理的时间内完成此任务的脚本。我们可以在晚上这样做,但最好不要超过2分钟。
BTW在未来我们可以构建某种机制来确定哪些记录已被解析,但这也需要某种形式的检测已解析的字段,这些字段在平均时间内已发生变化。我们还没有时间这样做,所以现在我们需要尽快进行原始解析。由于
答案 0 :(得分:1)
这是一种不同的方法。
代码可能不尽如人意,你可能需要修理一些小东西......
我在11g上检查了它(找不到10g)并将你的输入用作我的clob列中的值
对于10行(所有输入与您给出的输入相同),
原始查询: 9.8秒
新查询: 0.08秒
我使用了流水线功能,这里是代码:
create or replace type t_parse is object(idd number, levell number, answer_option varchar2(128), valuee varchar2(4000));
/
create or replace type tab_parse is table of t_parse;
/
create or replace function split_answers return tab_parse
pipelined is
cursor c is
select * from mneme_question;
str_t clob;
phraseP varchar2(128);
phraseV varchar2(8);
i1s number;
i1e number;
i2s number;
levell number;
begin
for r in c loop
str_t := r.guest;
levell := 1;
while str_t is not null loop
i1s := dbms_lob.instr(str_t, '<p>', 1, 1) + 3;
if i1s = 3 then
str_t := '';
else
i1e := dbms_lob.instr(str_t, '</p>', 1, 1);
phraseP := dbms_lob.substr(str_t, i1e - i1s, i1s);
str_t := dbms_lob.substr(str_t, offset => i1e + 4);
i2s := dbms_lob.instr(str_t, 'true', 1, 1) ;
if i2s = 0 then
i2s := dbms_lob.instr(str_t, 'false', 1, 1) ;
if i2s = 0 then
str_t := '';
else
phraseV := dbms_lob.substr(str_t, 5, i2s);
pipe row(t_parse(r.id, levell, phraseP, phraseV));
levell := levell + 1;
end if;
else
phraseV := dbms_lob.substr(str_t, 4, i2s);
pipe row(t_parse(r.id, levell, phraseP, phraseV));
levell := levell + 1;
end if;
end if;
end loop;
end loop;
return;
end split_answers;
/
新查询应该是:
select * from table(split_answers);
答案 1 :(得分:0)
有一些函数返回流水线表:
CREATE OR REPLACE FUNCTION split_clob(p_clob IN CLOB DEFAULT NULL,
p_varchar IN VARCHAR2 DEFAULT NULL,
p_separator IN VARCHAR2)
RETURN varchar_set
PIPELINED IS
l_clob CLOB;
BEGIN
l_clob := nvl(p_clob,
to_clob(p_varchar));
FOR rec IN (
WITH vals AS
(SELECT CAST(TRIM(regexp_substr(l_clob,
'[^'||p_separator||']+',
1,
levels.column_value)) AS
VARCHAR2(320)) AS val
FROM TABLE(CAST(MULTISET
(SELECT LEVEL
FROM dual
CONNECT BY LEVEL <=
length(regexp_replace(l_clob,
'[^'||p_separator||']+')) + 1) AS
sys.odcinumberlist)) levels)
SELECT val FROM vals)
LOOP
PIPE ROW(rec.val);
END LOOP;
RETURN;
END;
你可以像这样使用它:
- 带有clob的表
SELECT t2.column_value FROM my_table t, TABLE(split_clob(p_clob => t.clob_column,p_separator => ',')) t2
-or with varchar
SELECT * FROM TABLE(split_clob(p_varchar => '1,2,3,4,5,6, 7, 8, 9', p_separator => ','))