将CLOB字段拆分为表格式结构

时间:2012-03-12 09:19:19

标签: sql oracle10g

我在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在未来我们可以构建某种机制来确定哪些记录已被解析,但这也需要某种形式的检测已解析的字段,这些字段在平均时间内已发生变化。我们还没有时间这样做,所以现在我们需要尽快进行原始解析。

由于

2 个答案:

答案 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 => ','))
相关问题