【问题标题】:oracle query slow with REGEXP_SUBSTR(AGGREGATOR,'[^;]+',1,LEVEL)使用 REGEXP_SUBSTR(AGGREGATOR,'[^;]+',1,LEVEL) 的 oracle 查询速度很慢
【发布时间】:2021-03-12 01:29:02
【问题描述】:

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

表格如下所示:

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

【问题讨论】:

标签: sql oracle


【解决方案1】:

众所周知,正则表达式是昂贵的函数,因此当性能至关重要时(例如在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】:

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

    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 个 val - 查看第一个和第二个查询之间的区别..

    【讨论】:

    • 不错的解决方案,我忍不住将您的算法转换为递归 QTE 查询(您的函数仍然快一点:)
    • 顺便说一句,您可以使用横向连接来制作更通用的函数:SQLFiddle
    【解决方案3】:

    您的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
    

    【讨论】:

      【解决方案4】:

      我认为 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
      /
      

      【讨论】:

      • 你不能在没有CONNECT BY的情况下使用LEVEL,否则你会得到ORA-01788: CONNECT BY clause required in this query block
      • 我认为你错了,误读了我的回答或smth。 Where 子句将处理表中 AGGREGATOR IS NOT NULL 的所有行。不过没关系,由你决定。
      猜你喜欢
      • 2023-03-13
      • 1970-01-01
      • 2016-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多