2013-07-11 15 views
8

Używam Oracle 11g i chciałbym użyć REGEXP_SUBSTR do dopasowania wszystkich wystąpień dla danego wzorca. Dla przykładuOracle 11g uzyskuje wszystkie dopasowane wystąpienia przez wyrażenie regularne

SELECT 
    REGEXP_SUBSTR('Txa233141b Ta233141 Ta233142 Ta233147 Ta233148', 
    '(^|\s)[A-Za-z]{2}[0-9]{5,}(\s|$)') "REGEXP_SUBSTR" 
    FROM DUAL; 

powraca dopiero pierwszy mecz Ta233141 ale chciałbym wrócić innych zdarzeń, które pasują do wyrażenia regularnego, czyli Ta233142 Ta233147 Ta233148.

Odpowiedz

15

Funkcja REGEXP_SUBSTR zwraca tylko jedną wartość. Możesz przekształcić swój ciąg znaków w pseudo-tabelę, a następnie zapytać, czy pasuje. Tam jest sposób oparty na XML w ten sposób, że ucieka mi w tej chwili, ale przy użyciu prace łączą-by, tak długo, jak masz tylko jeden ciąg źródło:

SELECT REGEXP_SUBSTR(str, '[^ ]+', 1, LEVEL) AS substr 
FROM (
    SELECT 'Txa233141b Ta233141 Ta233142 Ta233147 Ta233148' AS str FROM DUAL 
) 
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(str, '[^ ]+')) + 1; 

... daje:

SUBSTR    
-------------------- 
Txa233141b   
Ta233141    
Ta233142    
Ta233147    
Ta233148    

... i można filtrować że z nieco prostszą wersją oryginalnego wzoru:

SELECT substr 
FROM (
    SELECT REGEXP_SUBSTR(str, '[^ ]+', 1, LEVEL) AS substr 
    FROM (
     SELECT 'Txa233141b Ta233141 Ta233142 Ta233147 Ta233148' AS str 
     FROM DUAL 
    ) 
    CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(str, '[^ ]+')) + 1 
) 
WHERE REGEXP_LIKE(substr, '^[A-Za-z]{2}[0-9]{5,}$'); 

SUBSTR    
-------------------- 
Ta233141    
Ta233142    
Ta233147    
Ta233148    

który nie jest bardzo ładny, ale nie jest posiadanie wielu wartości w jednym polu.

+0

Dziękuję bardzo. Bardzo mi to pomogło. – florins

+2

ten kod nie zwraca Ta233148. Aby uzyskać tę zmianę CONNECT BY LEVEL <= LENGTH (REGEXP_REPLACE (str, '[^] +')), aby połączyć się z POZIOMEM <= DŁUGOŚĆ (REGEXP_REPLACE (str, '[^] +')) + 1 – sakthi

+0

Korzystanie z tego kodu bezpośrednio z niczego oprócz prostego znaku rozdzielacza powróci do wielu wyników. Zamień LENGTH (REGEXP_REPLACE (str, '[^] +')) + 1 z REGEXP_COUNT (str, '[^] +'), a otrzymasz oczekiwany wynik. – bkqc

1

Co powiesz na dodanie funkcji, która przejdzie w pętlę i zwróci wszystkie wartości?

create or replace function regexp_substr_mr (
    p_data clob, 
    p_re varchar 
) 
return varchar as 
    v_cnt number; 
    v_results varchar(4000); 
begin 
    v_cnt := regexp_count(p_data, p_re, 1,'m'); 
    if v_cnt < 25 then 
    for i in 1..v_cnt loop 
     v_results := v_results || regexp_substr(p_data,p_re,1,i,'m') || chr(13) || chr(10); 
    end loop; 
    else 
    v_results := 'WARNING more than 25 matches found'; 
    end if; 

    return v_results; 
end; 

Następnie wystarczy wywołać funkcję jako część kwerendy wyboru.

+0

Zasadniczo preferuję to podejście, ponieważ moim zdaniem ma ono bardziej ogólne zastosowanie niż przyjęte rozwiązanie.Poprawiłbym go jednak przekazując p_data, p_re i flags i tworząc typ typu "stwórz lub zamień typ mytype to table of varchar2 (4000)" i zwróć go z funkcji. Następnie można go osadzić w SQL w następujący sposób: wybrać A.id, X.nazwa_kolumny z someTable Tabela łączenia krzyżowego (regexp_substr_mr (A.textToSearch, 'regexp') X – Pancho

-1

Poniżej znajduje się proste rozwiązanie dla Twojego pytania.

SELECT REGEXP_SUBSTR('Txa233141b Ta233141 Ta233142 Ta233147 Ta233148', 
    '([a-zA-Z0-9]+\s?){1,}') "REGEXP_SUBSTR" 
    FROM DUAL; 
+0

To po prostu zwraca oryginalny ciąg znaków, zawiera on nawet Txa233141b choć zaczyna się od trzech nie-numerycznych znaków, a nie dwóch, jak wymaga tego schemat OP. –

0

Jestem naprawić @Alex Poole answer wsparcia źródłowego multi-line i na szybsze wykonanie:

with templates as (select '\w+' regexp from dual) 
select 
    regexp_substr(str, templates.regexp, 1, level) substr 
from (
    select 1 id, 'Txa233141b Ta233141 Ta233142 Ta233147 Ta233148' as str from dual 
    union 
    select 2 id, '2 22222222222222Ta233141 2Ta233142 2Ta233147' as str from dual 
    union 
    select 3 id, '3Txa233141b 3Ta233141 3Ta233142' as str from dual 
) 
join templates on 1 = 1 
connect by 
    id = connect_by_root id 
    and regexp_instr(str, templates.regexp, 1, level) > 0 
order by id, level 

linie Źródło:

ID STR            
-- ---------------------------------------------- 
1 Txa233141b Ta233141 Ta233142 Ta233147 Ta233148 
2 2 22222222222222Ta233141 2Ta233142 2Ta233147  
3 3Txa233141b 3Ta233141 3Ta233142     

Wynik:

Txa233141b    
Ta233141     
Ta233142     
Ta233147     
Ta233148     
2      
22222222222222Ta233141 
2Ta233142    
2Ta233147    
3Txa233141b    
3Ta233141    
3Ta233142    
1

Thi s trochę się spóźniło, ale potrzebowałem w zasadzie tego samego i nie mogłem znaleźć dobrego fragmentu. Musiałem wyszukać kolumnę z danymi w tabeli dla niektórych haseł i je zebrać. Ponieważ może to być przydatne dla innej wersji, uwzględniłem wersję opartą na tym pytaniu. Podczas gdy REGEXP_SUBSTR zwraca tylko jedną wartość, Oracle zapewnia również REGEXP_COUNT, aby powiedzieć, ile pasujących elementów jest obecnych w danym ciągu, dlatego możesz dołączyć do tego z listą indeksów, aby wybrać każdy z nich w następujący sposób (uogólniony dla pojedynczej linii z góry jako the 'source_table'):

WITH source_table 
    AS (SELECT 'Txa233141b Ta233141 Ta233142 Ta233147 Ta233148' as free_text 
      FROM dual) 
    , source 
    AS (SELECT cnt 
       , free_text 
      FROM (SELECT RegExp_Count(free_text, '(^|\s)[A-Za-z]{2}[0-9]{5,}(\s|$)') AS cnt 
         , free_text 
       FROM source_table) 
      WHERE cnt > 0) 
    , iota 
    AS (SELECT RowNum AS idx 
      FROM dual 
      CONNECT BY RowNum <= (SELECT Max(cnt) FROM source)) 
SELECT UNIQUE 
     RegExp_SubStr(s.free_text, '(^|\s)[A-Za-z]{2}[0-9]{5,}(\s|$)', 1, i.idx) AS result 
FROM source s 
    JOIN iota i 
    ON (i.idx <= s.cnt)