使用REGEXP_SUBSTR进行oracle查询缓慢(AGGREGATOR,'[^;] +',1,LEVEL)

时间:2013-02-27 07:17:19

标签: sql oracle

您好我正在使用此查询来获取不同的行;分离值

表就像

row_id  aggregator
1             12;45
2             25

使用此查询我想要输出

row_id  aggregator
1        12
1        45
2        25

我正在使用以下查询

SELECT 
DISTINCT ROW_ID,  
REGEXP_SUBSTR(AGGREGATOR,'[^;]+',1,LEVEL) as AGGREGATOR,                       
FROM DUMMY_1 
CONNECT BY REGEXP_SUBSTR(AGGREGATOR,'[^;]+',1,LEVEL) IS NOT NULL;

但即使是300条记录也很慢 我必须为40000条记录工作。

4 个答案:

答案 0 :(得分:4)

有时流水线表可以更快,试试这个:

create or replace type t is object(word varchar2(100), pk number);
/
create or replace type t_tab as table of t;
/

create or replace function split_string(del in varchar2) return t_tab
  pipelined is

  word    varchar2(4000);
  str_t   varchar2(4000) ;
  v_del_i number;
  iid     number;

  cursor c is
    select * from DUMMY_1; 

begin

  for r in c loop
    str_t := r.aggregator;
    iid   := r.row_id;

    while str_t is not null loop

      v_del_i := instr(str_t, del, 1, 1);

      if v_del_i = 0 then
        word  := str_t;
        str_t := '';
      else
        word  := substr(str_t, 1, v_del_i - 1);
        str_t := substr(str_t, v_del_i + 1);
      end if;

      pipe row(t(word, iid));

    end loop;

  end loop;

  return;
end split_string;

Here is a sqlfiddle demo

And here is another demo包含22行,每行包含3个vals - 查看第一个和第二个查询之间的差异..

答案 1 :(得分:4)

已知正则表达式是昂贵的函数,因此在性能至关重要时(例如在CONNECT BY子句中使用标准函数),应尽量减少它们的使用。

使用标准函数(INSTRSUBSTRREPLACE)会更有效,但生成的代码将难以阅读/理解/维护。

我无法抗拒写一个递归QTE,我比正则表达式和标准函数更有效率。此外,递归QTE查询可以说是一种优雅。你需要Oracle 11.2:

WITH rec_sql(row_id, aggregator, lvl, tail) AS (
SELECT row_id, 
       nvl(substr(aggregator, 1, instr(aggregator, ';') - 1), 
           aggregator),
       1 lvl,
       CASE WHEN instr(aggregator, ';') > 0 THEN
          substr(aggregator, instr(aggregator, ';') + 1)
       END tail
  FROM dummy_1 initialization
UNION ALL
SELECT r.row_id, 
       nvl(substr(tail, 1, instr(tail, ';') - 1), tail), 
       lvl + 1, 
       CASE WHEN instr(tail, ';') > 0 THEN
          substr(tail, instr(tail, ';') + 1)
       END tail
  FROM rec_sql r
 WHERE r.tail IS NOT NULL
)
SELECT * FROM rec_sql;

您可以在SQLFiddle上看到此解决方案非常高效且与@A.B.Cade's solution相同。 (感谢A.B.Cade的测试用例)。

答案 2 :(得分:0)

您的connect by产生的记录比需要的多得多,这就是性能低下的原因,您需要使用distinct来限制记录数。确实需要distinct的方法是:

select row_id, regexp_substr(aggregator,'[^;]+',1,n) aggregator
  from dummy_1, (select level n from dual connect by level < 100)
 where n <= regexp_count(aggregator,';')+1

答案 3 :(得分:-1)

我认为DISTINCT可能存在问题。此外,我不明白为什么你需要CONNECT BY REGEXP_SUBSTR(AGGREGATOR,'[^;] +',1,LEVEL)IS NOT NULL。您在选择和连接中使用正则表达式。你可以使用AGGREGATOR IS NOT而不是connect by?找到一种方法来摆脱不同并修改您的查询。您可以使用EXISTS代替不同的...为了帮助您,我需要表格和数据。

SELECT * FROM
(
 SELECT REGEXP_SUBSTR(AGGREGATOR ,'[^;]+',1,LEVEL) as AGGREGATOR                      
   FROM your_table
)
WHERE AGGREGATOR IS NOT NULL
/