6

我必须在 toad 中编写一个 Oracle 查询来查找字符串中出现的所有字符。例如,如果我R在 string 中搜索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是一个无效的标识符。

4

3 回答 3

14

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

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

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

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

如果您要挑选的字符串更复杂,您可以使用正则表达式 ans 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
于 2013-07-28T08:51:10.353 回答
9

这是一个解决方案:

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'

于 2013-07-28T08:17:24.680 回答
4

在这里接受a_horse_with_no_name 的挑战是使用流水线表函数的另一个答案。

流水线函数返回一个数组,可以正常查询。我希望在具有大量匹配项的字符串上,这将比递归查询执行得更好,但就像一切都先测试自己一样。

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'))

SQL小提琴

于 2013-07-28T09:48:46.560 回答