3

我正在使用查询来获取不同的行而不是分号分隔的值。

该表如下所示:

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

4 回答 4

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;

这是一个 sqlfiddle 演示

这是另一个演示,其中包含 22 行,每行在聚合器中包含 3 个 val - 请参阅第一个和第二个查询之间的区别..

于 2013-02-27T07:30:52.160 回答
4

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

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

我忍不住写了一个递归 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上看到该解决方案非常高效,并且与@ABCade 的解决方案相当。(感谢 ABCade 的测试用例)。

于 2013-02-27T09:55:54.877 回答
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

如果分号数小于 99,则上述方法有效。以下解决方案没有此限制,如果最大分号数较低,则速度更快:

with dummy_c as (select row_id, aggregator, regexp_count(aggregator,';')+1 c from dummy_1)
select row_id, regexp_substr(aggregator,'[^;]+',1,n) aggregator
  from dummy_c, (select level n from dual connect by level <= (select max(c) from dummy_c))
 where n <= c
于 2018-06-25T13:35:07.370 回答
-1

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

SELECT * FROM
(
 SELECT REGEXP_SUBSTR(AGGREGATOR ,'[^;]+',1,LEVEL) as AGGREGATOR                      
   FROM your_table
)
WHERE AGGREGATOR IS NOT NULL
/
于 2013-02-27T20:35:07.740 回答