2013-02-27 272 views
3

嗨,我用这个查询来获取不同的行; seprate值oracle查询缓慢REGEXP_SUBSTR(AGGREGATOR,'[^;] +',1,LEVEL)

表就像

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条记录工作。

+0

可能重复的[SQL查询得到表中的单词数](http://stackoverflow.com/questions/9563944/sql-query-to-get-count-of-words-in-table ) – Ben 2013-02-27 08:38:31

回答

3

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

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用含3个瓦尔斯在聚合器各行22 - 看到第一和第二查询之间的差..

+1

这绝对是更高性能。 – Ben 2013-02-27 08:37:57

+0

不错的解决方案,我无法拒绝将您的算法转换为递归QTE查询(您的函数仍然更快:) :) – 2013-02-27 09:58:39

+0

顺便说一下,您可以使用横向连接来创建更通用的函数:[SQLFiddle](http:///www.sqlfiddle.com/#!4/40050/1)。 – 2013-02-27 11:05:22

3

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

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

我无法抗拒写递归QTE,我比正则表达式和标准函数更有效率。此外,递归QTE查询可以说有些优雅。你需要甲骨文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的测试用例)。

+0

+1伟大的解决方案!这一个应该使用,如果使用11gR2 – 2013-02-27 10:47:12

+0

棒极了!谢谢 – San 2015-08-24 13:29:04

0

我认为DISTINCT可能是问题所在。此外,我不明白为什么你需要连接REGEXP_SUBSTR(AGGREGATOR,'[^;] +',1,LEVEL)不是NULL。您在选择中使用regexp并通过连接。你可以使用AGGREGATOR不是NULL而不是通过连接?找到一种摆脱不同的方法并修改您的查询。你可以使用EXISTS而不是独特的...为了帮助你更多,我需要表格和数据。

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