2013-07-28 62 views
6

我必须在toad中编写Oracle查询以查找字符串中字符的所有出现。例如,如果我的字符串SSSRNNSRSSR在寻找R,应该归仓4,8和11Oracle查询查找字符串中所有出现的字符

我新的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是无效标识符。

+0

有趣的挑战。这将成为一个非常奇怪的查询,或者你将不得不编写一个存储过程。或者总是有最后的,不幸的可能性,有一些方便的功能,我不知道... – GolezTrol

+0

(自写)功能可能比使用任何种类的递归查询更有效。 –

+0

@a_horse_with_no_name - 它可能是,但不要低估调用函数的开销。不过,这将是一个有趣的测试,但目前我只有sqlfiddle,我怀疑这是否是一个可靠的基准测试平台。 – GolezTrol

回答

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'

13

所有行扩展GolezTrol的回答,您可以使用正则表达式来显著减少你做递归查询的数量的事情:

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

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

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 
+0

不错的解决方案,并且比较短。由于递归较少,我可能会更快,但另一方面,正则表达式比“简单”字符串操作要慢。想选择一个基准。 – GolezTrol

+0

这不是_That_太慢@GolezTrol;我已经完成了一个简单的基准测试,对于这个字符串和匹配次数,递归越少越好......随着字符串变小,它会减少(如果有的话)差异。 – Ben

+0

所以正则表达式更快,至少在这个尺寸的字符串上。有趣。感谢您的基准! – GolezTrol

3

占用a_horse_with_no_name's challenge这是一个带pipelined table function另一个答案。

流水线函数返回一个数组,您可以正常查询。我期望通过大量匹配的字符串,这将比递归查询更好地执行,但所有事情都是先测试自己。

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 Fiddle

相关问题