如何在oracle中选择子字符串?

时间:2014-09-10 14:36:46

标签: sql regex oracle substr

我的情况是我的数据如下所示:

  

第18章第10单元第16节

  • 案例1:我想从上面的字符串中选择第18章。
  • 案例2:我想从上面的字符串中选择Unit 10.
  • 案例3:我想从上面的字符串中选择第16节。

4 个答案:

答案 0 :(得分:4)

我会使用REGEXP_SUBSTRdocumentation)和正确的正则表达式。例如:

select regexp_substr('Chapter 18 Unit 10 Sect 16', 'Chapter \d*') from dual;
  --Will return: Chapter 18
select regexp_substr('Chapter 18 Unit 10 Sect 16', 'Unit \d*') from dual;
  --Will return: Unit 10
select regexp_substr('Chapter 18 Unit 10 Sect 16', 'Sect \d*') from dual;
  --Will return: Sect 16

当然,如果您在表中存储Chapter xx Unit yy Sect zz个字符串,那么您只需使用此类查询即可获得多个结果:

select regexp_substr(info_column, 'Chapter \d*') from mytable;

您可以将\d替换为[0-9][[:digit:]]

<强> SQLfiddle

答案 1 :(得分:2)

我会使用旧的SUBSTR而不是REGEXP。由于REGEXP将占用过多的CPU资源。

您的要求非常简单。

对于数据Chapter 18 Unit 10 Sect 16,如果您希望第18章作为输出:

执行:

Select substr(column, 1, 10) from table

同样,你可以为其他输出做。

编辑:伙计们,你们当中有些人可能会想,为什么我要强调好旧的SUBSTR而不是REGEXP。只需将跟踪打开,然后查看跟踪输出。我同意,对于较新的版本,Oracle已经对REGEXP做了很多改进。但是,到目前为止,我还没有看到一个令我满意的案例。我可能错了,所以,如果有人有测试用例,我真的很想看到它。这对我们所有人来说都是一个很好的学习。

更新以显示上述关于REGULAR EXPRESSION的测试用例比SUBSTR + INSTR更快,是错误的!

常规表达:

SQL> DECLARE
  2      l_start NUMBER := dbms_utility.get_cpu_time;
  3  BEGIN
  4      FOR i IN (WITH t
  5                     AS (SELECT 'Chapter '
  6                                || LEVEL
  7                                || ' Unit '
  8                                || LEVEL
  9                                || ' Sect '
 10                                || LEVEL d
 11                         FROM   dual
 12                         CONNECT BY ROWNUM < 100000)
 13                SELECT Regexp_substr(d, 'Chapter [0-9]*') chapter,
 14                       Regexp_substr(d, 'Unit [0-9]*')    unit,
 15                       Regexp_substr(d, 'Sect [0-9]*')    sect
 16                 FROM   t) LOOP
 17          NULL;
 18      END LOOP;
 19
 20      dbms_output.Put_line('time taken by REGULAR EXPRESSION : '
 21                           || ( dbms_utility.get_cpu_time - l_start )
 22                           || ' hsec');
 23  END;
 24
 25  /
time taken by REGULAR EXPRESSION : 61 hsec

PL/SQL procedure successfully completed.

SUBSTR + INSTR:

SQL> DECLARE
  2      l_start NUMBER := dbms_utility.get_cpu_time;
  3  BEGIN
  4      FOR i IN (WITH t
  5                     AS (SELECT 'Chapter '
  6                                || LEVEL
  7                                || ' Unit '
  8                                || LEVEL
  9                                || ' Sect '
 10                                || LEVEL d
 11                         FROM   dual
 12                         CONNECT BY ROWNUM < 100000)
 13                SELECT Substr(d, 1, Instr(d, ' ', 1, 2) - 1)
 14                       chapter,
 15                       Substr(d, Instr(d, ' ', 1, 2),
 16                       Instr(d, ' ', 1, 4) - Instr(d,
 17                       ' ', 1, 2))
 18                       unit,
 19                       Substr(d, Instr(d, ' ', 1, 4), Length(d) - Instr(d, ' ', 1,
 20                                                                  4)
 21                                                      + 1)
 22                       sect
 23                 FROM   t) LOOP
 24          NULL;
 25      END LOOP;
 26
 27      dbms_output.Put_line('time taken by SUBSTR + INSTR : '
 28                           || ( dbms_utility.get_cpu_time - l_start )
 29                           || ' hsec');
 30  END;
 31
 32  /
time taken by SUBSTR + INSTR : 28 hsec

PL/SQL procedure successfully completed.

因此,可以清楚地看到SUBSTR + INSTR所花费的时间不到常规表达的一半。

答案 2 :(得分:0)

我的版本,在数据长度未修复时很有用。这可能是泛型

regexp_substr的最后一个参数实际上返回了相应的案例结果!

SELECT level as case ,
       regexp_substr('Chapter 180 Unit 10 Sect 16 World 100', '\w* \d*( )*',1,level) as result
FROM dual
  CONNECT BY level <= CEIL(regexp_count('Chapter 180 Unit 10 Sect 16 World 100',' ')/2)

<强>结果:

      CASE RESULT
---------- ------------------------
         1 Chapter 180
         2 Unit 10
         3 Sect 16
         4 World 100

Fiddle Demo

答案 3 :(得分:0)

使用substr:

declare
  l_start number := DBMS_UTILITY.get_cpu_time;
begin
for i in (
with t as (
  select 'Chapter ' || level || ' Unit ' || level || ' Sect ' || level  d from dual connect by rownum < 100000
)
select substr(d, 1, instr(d, ' ', 1, 2) - 1) chapter
     , substr(d, 
          instr(d, ' ', 1, 2), 
          instr(d, ' ', 1, 4) - instr(d, ' ', 1, 2)
       ) unit
     , substr(d, 
          instr(d, ' ', 1, 4), 
          length(d) - instr(d, ' ', 1, 4) + 1
       ) sect 
  from t
)
loop
  null;
end loop;
 DBMS_OUTPUT.put_line((DBMS_UTILITY.get_cpu_time - l_start) || ' hsec');
end;

126 hsec

使用regexp:

declare
  l_start number := DBMS_UTILITY.get_cpu_time;
begin
for i in (
with t as (
  select 'Chapter ' || level || ' Unit ' || level || ' Sect ' || level  d from dual connect by rownum < 100000
)
select regexp_substr(d, 'Chapter [0-9]*') chapter
     , regexp_substr(d, 'Unit [0-9]*') unit
     , regexp_substr(d, 'Sect [0-9]*') sect 
  from t
)
loop
  null;
end loop;
 DBMS_OUTPUT.put_line((DBMS_UTILITY.get_cpu_time - l_start) || ' hsec');
end;

190 hsec

所以使用regexp的解决方案速度较慢,但​​它更具可读性,如果我是你,我会使用regexp。