2013-12-13 166 views
1

使用正則表達式我有以下作爲輸入提取逗號分隔值在Oracle

Str := "Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)" 

必出把

AB123,MN456,xy789 

我使用下面在Oracle正則表達式

SELECT TRIM (
      REGEXP_SUBSTR (
      'Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)', 
      '[[:alpha:]]{2}[[:digit:]]{3}', 
      1, 
      1, 
      'i')) 
    FROM DUAL; 

它返回我只值AB123我希望所有用逗號分隔。

請幫忙

在此先感謝。

回答

2

這麼複雜的答案...

有更簡單的一個:

select rtrim(regexp_replace('Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)', 
          '([^\(]+?\(([[:alpha:]]{2}[[:digit:]]{3})\))','\2,',1,0,'i'),',') 
from dual; 

希望這有助於。

編輯: 有點改變的版本:

select rtrim(regexp_replace('Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)', 
          '[^\(]+?\(([[:alpha:]]{2}[[:digit:]]{3})\)','\1,',1,0,'i'),',') 
from dual; 
+0

嘿,西蒙你能否請解釋上面的答案,因爲我是REGEX的新手。從第一眼看來,似乎你將從Name1 Surname1(AB123)替換除AB123以外的所有東西。 – jaychapani

+0

是的,沒錯! '[^ \(] +?\('表示所有內容直到「(」,'([[:alpha:] {2} [[:digit:]] {3} 「用作\ 2和'\)'是當前塊的結尾(在這種情況下你有3個) – smnbbrv

+0

,不需要使用outer():可以刪除,並且\ 2應該被改變然後到\ 1.更新答案 – smnbbrv

0

難看一點是肯定的,並僅適用於Oracle版本> = 11.2(因爲LISTAGG是自認爲引入):

SELECT LISTAGG(COL1, ',') WITHIN GROUP(ORDER BY 1) RESULT 
    FROM (SELECT TRIM(REGEXP_SUBSTR('Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)', 
            '[[:alpha:]]{2}[[:digit:]]{3}', 
            1, 
            ROWNUM, 
            'i')) COL1 
      FROM DUAL 
     CONNECT BY LEVEL <= REGEXP_COUNT('Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)', 
             '[[:alpha:]]{2}[[:digit:]]{3}', 
             1, 
             'i')); 

RESULT 
-------------------------------------------------------------------------------- 
AB123,MN456,xy789 

注:上述工程與patte的任何出現次數在輸入字符串中。

更新:對於版本9i,10g,11.1,您可以使用Tom Kyte貢獻的STRAGG user function。正如它在評論中提到的那樣,還有WM_CONCAT功能。

+1

您可以用'WM_CONCAT'的10G – smnbbrv

+0

它也如果你真的有很大的投入,那就不是那麼高效了。 – smnbbrv

+0

當然,Tom Kyte也有這樣的分數。我會將其添加到答案中。關於性能問題,這都是相對的。讓我們看看另一個解決方案並進行比較。 –

0

不是非常好,但工程。

SELECT REGEXP_REPLACE 
('Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)', 
'^.*?(\([^)]*?\)).*?(\([^)]*?\)).*?(\([^)]*?\))','\1,\2,\3') 
FROM DUAL; 
+0

以及如果我再添加一個「Name3 Surname3(xy789)」會怎麼樣? – smnbbrv

+0

你將不得不修改正則表達式。 – tvm

+0

正則表達式應該像常量 - 獨立於輸入 – smnbbrv

1

SQL Fiddle

查詢1

這是如何使用正則表達式替換做到這一點:

(和一些邊緣的情況下再次進行測試 - NULL姓氏,後綴加上姓氏和雙管姓)

WITH strings AS (
      SELECT 'Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)' AS str FROM DUAL 
    UNION ALL SELECT 'Madonna (MA001), John Jones(Jr) (JJ001), Doctor Doctor(PhD) (dd001), Alf Double-Barrelled (AD001)' AS str FROM DUAL 
) 
SELECT REGEXP_REPLACE(str, '.*?\(([[:alpha:]]{2}[[:digit:]]{3})\)\s*(,|$)', '\1\2') AS match 
FROM strings 

Results

|     MATCH | 
|-------------------------| 
|  AB123,MN456,xy789 | 
| MA001,JJ001,dd001,AD001 | 

查詢2

這是如何使用分層查詢做到這一點:

WITH str AS (
    SELECT 'Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)' AS str 
    FROM DUAL 
), 
lengths AS (
    SELECT str, 
     REGEXP_COUNT(str, '\(([[:alpha:]]{2}[[:digit:]]{3})\)\s*(,|$)') AS len 
    FROM str 
) 
SELECT SUBSTR(
     SYS_CONNECT_BY_PATH (
      REGEXP_SUBSTR (
       str, 
       '\(([[:alpha:]]{2}[[:digit:]]{3})\)\s*(,|$)', 
       1, 
       LEVEL, 
       NULL, 
       1 
      ), 
      ',' 
     ), 
     2 
     ) AS match 
FROM lengths 
WHERE LEVEL = len 
CONNECT BY LEVEL <= len 

Results

|    MATCH | 
|-------------------| 
| AB123,MN456,xy789 | 

查詢3

如果您正在使用版本的Oracle,其中預日期REGEXP_COUNT那麼你可以使用的LENGTH並在其位置REGEXP_REPLACE的組合;像這樣:

WITH str AS (
    SELECT 'Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)' AS str 
    FROM DUAL 
) 
SELECT str, 
     REGEXP_COUNT(str, '\(([[:alpha:]]{2}[[:digit:]]{3})\)\s*(,|$)') AS len, 
     LENGTH(REGEXP_REPLACE(str, '.*?\(([[:alpha:]]{2}[[:digit:]]{3})\)\s*(,|$)', 'X')) AS len2 
FROM str 

Results

|                 STR | LEN | LEN2 | 
|-----------------------------------------------------------------------|-----|------| 
| Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789) | 3 | 3 | 
2

我會做這樣的,試圖在Oracle 10.2:

SELECT regexp_replace 
     (
     'Name1 Surname1 (AB123), Name2 Surname2 (MN456), Name3 Surname3(xy789)' 
     ,' ?\w+ \w+ ?\(([^)]+)\)' 
     ,'\1' 
     ) as col 
    FROM dual; 
+0

真的很不錯,但有點不同於作者的定義... – smnbbrv

+0

有什麼不同?它給了我字符串:AB123,MN456,xy789 - 似乎是他想要的。 – Kossak

+0

是的,它的工作原理對於這個例子,我沒有冒犯你的解決方案,即使是這樣,但是作者的定義是獲得所有符合這個值的值:_ [[:alpha:]] {2} [[:digit:]] {3} _ – smnbbrv