Oracle查询以查找字符串中出现的所有字符

时间:2013-07-28 07:45:20

标签: sql oracle

我必须在toad中编写一个Oracle查询来查找字符串中所有出现的字符。例如,如果我在字符串R中搜索SSSRNNSRSSR,则应返回位置4,8和11。

我是Oracle的新手并试过这个。

select instr(mtr_ctrl_flags, 'R', pos + 1, 1) as pos1 
  from mer_trans_reject  
 where pos in ( select instr(mtr_ctrl_flags, 'R', 1, 1) as pos 
                  from mer_trans_reject
                       );

其中mtr_ctrl_flags是列名。我收到错误消息,指出pos是无效的标识符。

3 个答案:

答案 0 :(得分:13)

扩展GolezTrol的答案,您可以使用正则表达式来显着减少您执行的递归查询的数量:

 select instr('SSSRNNSRSSR','R', 1, level)
   from dual
connect by level <= regexp_count('SSSRNNSRSSR', 'R')

REGEXP_COUNT()返回模式匹配的次数,在这种情况下,R中存在SSSRNNSRSSR的次数。这会将递归级别限制为您需要的确切数字。

INSTR()只是在字符串中搜索R的索引。 level是递归的深度,但在这种情况下,它也是字符串的 th 级别,因为我们限制了所需的递归次数。

如果您想要挑选的字符串更复杂,那么您可以使用正则表达式REGEXP_INSTR()而不是INSTR(),但它会更慢(不是太多)并且除非需要,否则它是不必要的。


根据要求提供简单的基准:

两个CONNECT BY解决方案表明,对于此大小的字符串,使用REGEXP_COUNT的速度要快20%。

SQL> set timing on
SQL>
SQL> -- CONNECT BY with REGEX
SQL> declare
  2     type t__num is table of number index by binary_integer;
  3     t_num t__num;
  4  begin
  5    for i in 1 .. 100000 loop
  6       select instr('SSSRNNSRSSR','R', 1, level)
  7         bulk collect into t_num
  8         from dual
  9      connect by level <= regexp_count('SSSRNNSRSSR', 'R')
 10              ;
 11     end loop;
 12  end;
 13  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:03.94
SQL>
SQL> -- CONNECT BY with filter
SQL> declare
  2     type t__num is table of number index by binary_integer;
  3     t_num t__num;
  4  begin
  5    for i in 1 .. 100000 loop
  6       select pos
  7         bulk collect into t_num
  8         from ( select substr('SSSRNNSRSSR', level, 1) as character
  9                     , level as pos
 10                  from dual t
 11               connect by level <= length('SSSRNNSRSSR') )
 12        where character = 'R'
 13              ;
 14     end loop;
 15  end;
 16  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:04.80

流水线表函数有点慢,但看看它如何在大量字符串上执行有很多匹配会很有趣。

SQL> -- PIPELINED TABLE FUNCTION
SQL> declare
  2     type t__num is table of number index by binary_integer;
  3     t_num t__num;
  4  begin
  5    for i in 1 .. 100000 loop
  6       select *
  7         bulk collect into t_num
  8         from table(string_indexes('SSSRNNSRSSR','R'))
  9              ;
 10     end loop;
 11  end;
 12  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:06.54

答案 1 :(得分:8)

这是一个解决方案:

select
  pos
from
  (select
    substr('SSSRNNSRSSR', level, 1) as character,
    level as pos
  from
    dual
  connect by
    level <= length(t.text))
where
  character = 'R'

dual是一个内置表,只返回一行。很方便!

connect by允许您构建递归查询。这通常用于从树状数据(父/子关系)生成列表。它允许您或多或少地在它前面重复查询。而且你有一些特殊的字段,比如level,可以让你检查递归的深度。

在这种情况下,我使用它将字符串拆分为字符并为每个字符返回一行。使用level,我可以重复查询并获取一个字符,直到到达字符串的末尾。

然后只需返回包含字符pos的所有行的'R'

答案 2 :(得分:3)

a_horse_with_no_name's challenge取消pipelined table functionSQL Fiddle的另一个答案。

pipelined函数返回一个数组,您可以正常查询。我希望在具有大量匹配的字符串上,这将比递归查询表现得更好,但与所有内容一样先测试自己。

create type num_array as table of number
/


create function string_indexes (
    PSource_String in varchar2
  , PSearch_String in varchar2
    ) return num_array pipelined is

begin

   for i in 1 .. length(PSource_String) loop

      if substr(PSource_String, i, 1) = PSearch_String then
         pipe row(i);
      end if;

   end loop;

   return;

end;
/

然后为了访问它:

select *
  from table(string_indexes('SSSRNNSRSSR','R'))

{{3}}