当前位置: 首页 > 面试题库 >

REGEXP_SUBSTR与INSTR和SUBSTR的性能和可读性

郝冥夜
2023-03-14
问题内容

由此产生的数据集只应delimters之前显示的字符串PLE#ALL以正确的顺序。程序包中已经存在的“当前查询”是这样的(DDL和DML在帖子的底部):

SELECT  DATA1
      , DECODE(SIGN(0 - instr(DATA1, 'PLE')), -1, SUBSTR(DATA1, 1, instr(DATA1, 'PLE') - 1)) GET_DATA_TILL_FIRST_PLE
      , DECODE(SIGN(0 - instr(DATA1, '#')), -1, SUBSTR(DATA1, 1, instr(DATA1, '#') - 1)) GET_DATA_TILL_FIRST_NUM_SIGN
      , DECODE(SIGN(0 - instr(DATA1, 'ALL')), -1, SUBSTR(DATA1, 1, instr(DATA1, 'ALL') - 1)) GET_DATA_TILL_FIRST_ALL
      , NVL(DECODE(SIGN(0 - instr(DATA1, 'PLE')), -1, SUBSTR(DATA1, 1, instr(DATA1, 'PLE') - 1), 0,
        DECODE(SIGN(0 - instr(DATA1, '#')), -1, SUBSTR(DATA1, 1, instr(DATA1, '#') - 1), 0,
        DECODE(SIGN(0 - instr(DATA1, 'ALL')), -1, SUBSTR(DATA1, 1, instr(DATA1, 'ALL') - 1), DATA1), DATA1), DATA1), DATA1) PUT_THEM_ALL_TOGETHER    
FROM    table_x;

哪些结果会生成以下数据集:

DATA1                   | GET_DATA_TILL_FIRST_PLE | GET_DATA_TILL_FIRST_#_SIGN  | GET_DATA_TILL_FIRST_ALL    |  PUT_THEM_ALL_TOGETHER
----------------------- | ----------------------- | --------------------------- | -------------------------- |  ----------------------
STRING_EXAMPLE          | STRING_EXAM             |                             |                            |  STRING_EXAM
TREE_OF_APPLES          | TREE_OF_AP              |                             |                            |  TREE_OF_AP
FIRST_EXAMPLE           | FIRST_EXAM              |                             |                            |  FIRST_EXAM
IMPLEMENTATION          | IM                      |                             |                            |  IM
PARIS                   |                         |                             |                            |  PARIS
PLEONASM                |                         |                             |                            |  PLEONASM
XXXX 1                  |                         |                             |                            |  XXXX 1 
XXXX YYYYYY 2 FFFFFFFFF |                         |                             |                            |  XXXX YYYYYY 2 FFFFFFFFF
XXXX YYYYYY 5FFFFFFFFF  |                         |                             |                            |  XXXX YYYYYY 5FFFFFFFFF
OPOPOPOPO #09090 APPLE  | OPOPOPOPO #09090 AP     | OPOPOPOPO                   | OPOPOPOPO #                |  OPOPOPOPO #09090 AP
OPOPOPOPO BALL#         |                         | OPOPOPOPO BALL              | OPOPOPOPO B                |  OPOPOPOPO BALL
BALL IS #LIFE           |                         | BALL IS                     | B                          |  BALL IS

PS。 我只需要专栏,PUT_THEM_ALL_TOGETHER但我也包括其他专栏,但确实添加了上下文。

我发现查询有点混乱并且难以阅读,因此我尝试使用REGEXP_SUBSTR@vkp并提出了建议,我想出了以下查询,结果与上面的数据集相同。

SELECT  DATA1
  , REGEXP_SUBSTR(DATA1, '(.+?)PLE',1,1,null,1) GET_DATA_TILL_FIRST_PLE
  , REGEXP_SUBSTR(DATA1, '(.+?)#',1,1,null,1) GET_DATA_TILL_FIRST_#_SIGN
  , REGEXP_SUBSTR(DATA1, '(.+?)ALL',1,1,null,1) GET_DATA_TILL_FIRST_ALL
  , COALESCE(REGEXP_SUBSTR(DATA1, '(.+?)PLE',1,1,null,1),
             REGEXP_SUBSTR(DATA1, '(.+?)#',1,1,null,1),
             REGEXP_SUBSTR(DATA1, '(.+?)ALL',1,1,null,1),
             DATA1) PUT_THEM_ALL_TOGETHER
FROM    table_x;

然而,从@ MathGuy的答案,似乎INSTRSUBSTR有效得多。我在某种程度上进行了测试,这就是我得到的:

使用INSTRSUBSTR

SET TIMING ON;    
BEGIN
    UPDATE  table_x
    SET     DATA2 = NVL(DECODE(SIGN(0 - instr(DATA1, 'PLE')), -1, SUBSTR(DATA1, 1, instr(DATA1, 'PLE') - 1), 0,
                    DECODE(SIGN(0 - instr(DATA1, '#')), -1, SUBSTR(DATA1, 1, instr(DATA1, '#') - 1), 0,
                    DECODE(SIGN(0 - instr(DATA1, 'ALL')), -1, SUBSTR(DATA1, 1, instr(DATA1, 'ALL') - 1), DATA1), DATA1), DATA1), DATA1);    
    ROLLBACK;        
END;
/

PL / SQL过程成功完成。
播放时间:00:00:00.234

使用REGEXP_SUBSTR

SET TIMING ON;  
BEGIN    
    UPDATE  table_x
    SET     DATA2 = COALESCE(REGEXP_SUBSTR(DATA1, '(.+?)PLE',1,1,null,1)
                            ,REGEXP_SUBSTR(DATA1, '(.+?)#',1,1,null,1)
                            ,REGEXP_SUBSTR(DATA1, '(.+?)ALL',1,1,null,1)
                            ,DATA1);
    ROLLBACK;        
END;
/

PL / SQL过程成功完成。
播放时间:00:00:00.236

虽然这是一个非常有限的测试数据,它表明的组合INSTRSUBSTR一点点的速度比REGEXP_SUBSTR。难道是可以忽略不计在使用REGEXP_SUBSTR替代INSTRSUBSTR提高可读性的缘故?

DML和DDL:

create table table_x 
(
    data1 varchar2(100)    
   ,data2 varchar2(100)
);

INSERT INTO table_x (DATA1) VALUES ('STRING_EXAMPLE');
INSERT INTO table_x (DATA1) VALUES ('TREE_OF_APPLES');
INSERT INTO table_x (DATA1) VALUES ('FIRST_EXAMPLE');  
INSERT INTO table_x (DATA1) VALUES ('IMPLEMENTATION');   
INSERT INTO table_x (DATA1) VALUES ('PARIS');            
INSERT INTO table_x (DATA1) VALUES ('PLEONASM');

INSERT INTO table_x (DATA1) VALUES ('XXXX 1');   
INSERT INTO table_x (DATA1) VALUES ('XXXX YYYYYY 2 FFFFFFFFF'); 
INSERT INTO table_x (DATA1) VALUES ('XXXX YYYYYY 5FFFFFFFFF');

INSERT INTO table_x (DATA1) VALUES ('OPOPOPOPO #09090 APPLE'); 
INSERT INTO table_x (DATA1) VALUES ('OPOPOPOPO BALL#'); 
INSERT INTO table_x (DATA1) VALUES ('BALL IS #LIFE');

谢谢你。


问题答案:

我已经张贴出如何解决使用这一问题的答案INSTR,并SUBSTR以正确的方式。

在本“答案”中,我解决了另一个问题-哪种解决方案更有效。我将在下面解释测试,但这是最重要的一点:REGEXP解决方案花费的 时间
INSTR/SUBSTR解决方案 长40倍

设置
:我创建了一个包含150万个随机字符串的表(所有字符串长度均为8个字符,全部为大写字母)。然后,我修改了10%的字符串以添加子字符串'PLE',再修改了10%的添加了a
'#',再修改了10%的添加'ALL'。我这样做的分裂在位置上的原始字符串mod(rownum, 9)-这是0和8之间的数字-
和连接'PLE''#''ALL'在该位置。当然,这不是获取我们所需测试数据的最有效或最优雅的方法,但这无关紧要-
关键是创建测试数据并将其用于我们的测试中。

所以:现在我们有了一个只有一列的表,其中data1有150万行中的一些随机字符串。各10%的子串PLE#ALL在其中。

测试包括创建data2与原始帖子中相同的新字符串。我没有将结果插入表中;而是将结果插入表中。无论如何data2计算, 其重新
插入 表中的时间都应该相同。

相反,我将主查询放在一个外部查询中,该查询用于计算结果data2值的长度之和。这样,我保证优化器不能采用快捷方式:data2必须生成所有值,必须测量它们的长度,然后将它们求和。

下面是创建基表所需的语句,我将其称为table_z,然后运行了查询。

create table table_z as
select dbms_random.string('U', 8) as data1 from dual
connect by level <= 1500000;

update table_z 
set data1 = case
when rownum between      1 and 150000 then substr(data1, 1, mod(rownum, 9)) 
                               || 'PLE' || substr(data1, mod(rownum, 9) + 1)
when rownum between 150001 and 300000 then substr(data1, 1, mod(rownum, 9)) 
                               || '#'   || substr(data1, mod(rownum, 9) + 1)
when rownum between 300001 and 450000 then substr(data1, 1, mod(rownum, 9)) 
                               || 'ALL' || substr(data1, mod(rownum, 9) + 1)
          end
where rownum <= 450000;

commit;

INSTR/SUBSTR 解决方案

select sum(length(data2))
from (
select data1, 
       case 
         when instr(data1, 'PLE', 2) > 0 then substr(data1, 1, instr(data1, 'PLE', 2) - 1)
         when instr(data1, '#'  , 2) > 0 then substr(data1, 1, instr(data1, '#'  , 2) - 1)
         when instr(data1, 'ALL', 2) > 0 then substr(data1, 1, instr(data1, 'ALL', 2) - 1)
         else data1 end
       as data2
from   table_z
);

SUM(LENGTH(DATA2))
------------------
          10713352

1 row selected.

Elapsed: 00:00:00.73

REGEXP 解决方案

select sum(length(data2))
from (
select data1, 
       COALESCE(REGEXP_SUBSTR(DATA1, '(.+?)PLE',1,1,null,1)
                            ,REGEXP_SUBSTR(DATA1, '(.+?)#',1,1,null,1)
                            ,REGEXP_SUBSTR(DATA1, '(.+?)ALL',1,1,null,1)
                            ,DATA1)
       as data2
from   table_z
);

SUM(LENGTH(DATA2))
------------------
          10713352

1 row selected.

Elapsed: 00:00:30.75

在任何人提出这些建议之前,我都重复了两次查询;第一个解决方案的运行时间通常为0.75到0.80秒,第二个查询的运行时间为30到35秒。慢40倍以上。(因此,编译器/优化器花费时间来编译查询不是问题;这实际上是执行时间。)而且,这与从基表中读取150万个值无关,这与两种测试的时间都比处理要少得多。无论如何,我都会先运行INSTR/SUBSTR查询,因此,如果有任何缓存,那么该REGEXP查询将是一个受益的地方。

编辑
:我只是想出了建议的REGEXP解决方案中的一个低效率。如果我们将搜索模式锚定到字符串的开头(例如'^(.+?)PLE',注意^锚点),则REGEXP查询的运行时间将从30秒降至10秒。显然,Oracle实现不足以识别这种等效性,并尝试从第二个字符,第三个字符等进行搜索。执行时间仍然要长15倍;15
<40,但仍然相差很大。



 类似资料:
  • 本文向大家介绍Oracle的substr和instr函数简单用法,包括了Oracle的substr和instr函数简单用法的使用技巧和注意事项,需要的朋友参考一下 Oracle的substr函数简单用法 substr(字符串,截取开始位置,截取长度) //返回截取的字 substr('Hello World',0,1) //返回结果为 'H'  *从字符串第一个字符开始截取长度为1的字符串 sub

  • 本文向大家介绍Oracle中instr和substr存储过程详解,包括了Oracle中instr和substr存储过程详解的使用技巧和注意事项,需要的朋友参考一下 instr和substr存储过程,分析内部大对象的内容 instr函数 instr函数用于从指定的位置开始,从大型对象中查找第N个与模式匹配的字符串。 用于查找内部大对象中的字符串的instr函数语法如下:  lob_loc为内部大对象

  • 本文向大家介绍Oracle中的INSTR,NVL和SUBSTR函数的用法详解,包括了Oracle中的INSTR,NVL和SUBSTR函数的用法详解的使用技巧和注意事项,需要的朋友参考一下 Oracle中INSTR的用法: INSTR方法的格式为 INSTR(源字符串, 要查找的字符串, 从第几个字符开始, 要找到第几个匹配的序号) 返回找到的位置,如果找不到则返回0. 例如:INSTR('CORP

  • 我有一个PL/SQL过程,它对参数执行大量s。我想删除长度限制,所以尝试将其更改为。 工作正常,但性能受到影响,所以我做了一些测试(基于2005年的这些测试)。 更新:我可以用不同的Oracle版本和不同的硬件在几个不同的实例上重现这个过程,总是明显比慢,而且比慢得多。 Bob的结果和上面链接中的测试讲述了一个不同的故事。 有人能解释这一点吗,或者至少重现鲍勃或我的结果吗?谢了! 测试结果:

  • 问题内容: 尝试从Redis排序集中读取1M记录时突然遇到性能问题。我使用的光标和批处理大小为5K。 代码是在托管Redis的同一台计算机上使用Erlang R14执行的。批量接收5K元素大约需要1秒。不幸的是,我无法在这台机器上编译Erlang R16,但是我认为这没有关系。 为了进行比较,带有node_redis(hiredis解析器)的Node.js代码在2秒内达到了1M。Python和PH

  • 问题内容: 我不知道我是否是唯一知道这一点的人,但是枚举的值不是隐式最终的,可以修改。 这些值通常是在实例创建()时初始化的,但是除了我自己,我从未见过有人使用final关键字来表示应为不变的枚举变量。这不是问题的重点,只是想知道我是否是唯一意识到这一点的人。 我想知道的是,是否存在用于创建可变枚举的用例? 而且我还想知道我们可以使用枚举(无论是否使用良好实践)的局限性。我还没有测试过,但是可以用