2013-07-28 270 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

相關問題