2014-09-10 28 views
-4

我有場景,我的數據是一樣的東西如下:如何在oracle中選擇子字符串?

Chapter 18 Unit 10 Sect 16 

Case 1 : I want to select Chapter 18 from the above string. 
Case 2 : I want to select Unit 10 from the above string. 
Case 3 : I want to select Sect 16 from the above string. 

感謝&問候, Keerthi

+0

是數字常量?它能成爲「第45章145單元2」嗎?如何區分大小寫? – zaratustra 2014-09-10 14:50:57

+0

你試過了什麼?在stackoverflow上有很多很多類似的問題。 – 2014-09-10 15:19:16

回答

0

使用SUBSTR:

declare 
    l_start number := DBMS_UTILITY.get_cpu_time; 
begin 
for i in (
with t as (
    select 'Chapter ' || level || ' Unit ' || level || ' Sect ' || level d from dual connect by rownum < 100000 
) 
select substr(d, 1, instr(d, ' ', 1, 2) - 1) chapter 
    , substr(d, 
      instr(d, ' ', 1, 2), 
      instr(d, ' ', 1, 4) - instr(d, ' ', 1, 2) 
     ) unit 
    , substr(d, 
      instr(d, ' ', 1, 4), 
      length(d) - instr(d, ' ', 1, 4) + 1 
     ) sect 
    from t 
) 
loop 
    null; 
end loop; 
DBMS_OUTPUT.put_line((DBMS_UTILITY.get_cpu_time - l_start) || ' hsec'); 
end; 

126 hsec 

使用正則表達式:

declare 
    l_start number := DBMS_UTILITY.get_cpu_time; 
begin 
for i in (
with t as (
    select 'Chapter ' || level || ' Unit ' || level || ' Sect ' || level d from dual connect by rownum < 100000 
) 
select regexp_substr(d, 'Chapter [0-9]*') chapter 
    , regexp_substr(d, 'Unit [0-9]*') unit 
    , regexp_substr(d, 'Sect [0-9]*') sect 
    from t 
) 
loop 
    null; 
end loop; 
DBMS_OUTPUT.put_line((DBMS_UTILITY.get_cpu_time - l_start) || ' hsec'); 
end; 

190 hsec 

所以使用正則表達式的解決方案較慢,但它更具可讀性,如果我是你,我會使用正則表達式。

+0

這是完全錯誤的測試用例。您的REGEXP查詢不使用'd'列,而只是硬編碼'第18章10單元16'。只需將其替換爲d列並查看其差異即可。我將編輯我的帖子以添加您的測試用例來證明我的陳述,即REGEXP將會變得更慢並且會消耗更多資源。 – 2014-09-11 08:40:38

+0

我已更新,檢查我的鍋。 – 2014-09-11 08:53:00

+0

在新的更新中,也許你複製粘貼正則表達式中的substr代碼,所以兩者都是相同的代碼。您可能需要重新編輯。 – 2014-09-12 03:48:15

4

我會用REGEXP_SUBSTRdocumentation),右正則表達式。例如:

select regexp_substr('Chapter 18 Unit 10 Sect 16', 'Chapter \d*') from dual; 
    --Will return: Chapter 18 
select regexp_substr('Chapter 18 Unit 10 Sect 16', 'Unit \d*') from dual; 
    --Will return: Unit 10 
select regexp_substr('Chapter 18 Unit 10 Sect 16', 'Sect \d*') from dual; 
    --Will return: Sect 16 

當然,如果你存儲在表Chapter xx Unit yy Sect zz字符串,那麼你只需使用這種查詢獲得多個結果:

select regexp_substr(info_column, 'Chapter \d*') from mytable; 

您可以[0-9][[:digit:]]

更換 \d

SQLfiddle

+2

正則表達式沒有問題,直到遇到CPU資源消耗過高的問題。我寧願使用舊的SUBSTR來實現這樣簡單的要求。 – 2014-09-10 14:52:27

+0

當然,如果所討論的字符串中的值總是兩位數,我會建議普通的'substr'。 – yamny 2014-09-10 17:09:36

+0

查看我的更新。我使用zaratustra的測試用例顯示REGEXP較慢。他的測試案例不正確,糾正後顯示新的執行時間以證明事實。 – 2014-09-11 09:21:52

2

我會使用舊的SUBSTR而不是REGEXP。由於REGEXP將佔用太多的CPU資源。

您的要求很簡單。

對於數據爲Chapter 18 Unit 10 Sect 16,如果你想第18章作爲輸出:

只需執行:

Select substr(column, 1, 10) from table

同樣,您可以爲其他輸出做。

編輯:夥計們,你們中的一些人可能會認爲我爲什麼在REGEXP上強調老的SUBSTR。只需追蹤一下,然後查看跟蹤輸出。我同意,對於更新的版本,Oracle已經對REGEXP做了很多改進。但是,直到日期我還沒有看到一個我滿意的情況。我可能是錯的,所以,如果有人有測試用例,我真的很想看到它。這對我們所有人來說都是一次很好的學習。

更新顯示關於REGULAR EXPRESSION的上述測試用例比SUBSTR + INSTR更快,是錯誤的!

正則表達式:

SQL> DECLARE 
    2  l_start NUMBER := dbms_utility.get_cpu_time; 
    3 BEGIN 
    4  FOR i IN (WITH t 
    5      AS (SELECT 'Chapter ' 
    6        || LEVEL 
    7        || ' Unit ' 
    8        || LEVEL 
    9        || ' Sect ' 
10        || LEVEL d 
11       FROM dual 
12       CONNECT BY ROWNUM < 100000) 
13    SELECT Regexp_substr(d, 'Chapter [0-9]*') chapter, 
14      Regexp_substr(d, 'Unit [0-9]*') unit, 
15      Regexp_substr(d, 'Sect [0-9]*') sect 
16     FROM t) LOOP 
17   NULL; 
18  END LOOP; 
19 
20  dbms_output.Put_line('time taken by REGULAR EXPRESSION : ' 
21       || (dbms_utility.get_cpu_time - l_start) 
22       || ' hsec'); 
23 END; 
24 
25/
time taken by REGULAR EXPRESSION : 61 hsec 

PL/SQL procedure successfully completed. 

SUBSTR + INSTR:

SQL> DECLARE 
    2  l_start NUMBER := dbms_utility.get_cpu_time; 
    3 BEGIN 
    4  FOR i IN (WITH t 
    5      AS (SELECT 'Chapter ' 
    6        || LEVEL 
    7        || ' Unit ' 
    8        || LEVEL 
    9        || ' Sect ' 
10        || LEVEL d 
11       FROM dual 
12       CONNECT BY ROWNUM < 100000) 
13    SELECT Substr(d, 1, Instr(d, ' ', 1, 2) - 1) 
14      chapter, 
15      Substr(d, Instr(d, ' ', 1, 2), 
16      Instr(d, ' ', 1, 4) - Instr(d, 
17      ' ', 1, 2)) 
18      unit, 
19      Substr(d, Instr(d, ' ', 1, 4), Length(d) - Instr(d, ' ', 1, 
20                 4) 
21              + 1) 
22      sect 
23     FROM t) LOOP 
24   NULL; 
25  END LOOP; 
26 
27  dbms_output.Put_line('time taken by SUBSTR + INSTR : ' 
28       || (dbms_utility.get_cpu_time - l_start) 
29       || ' hsec'); 
30 END; 
31 
32/
time taken by SUBSTR + INSTR : 28 hsec 

PL/SQL procedure successfully completed. 

因此,它可以清楚地看到SUBSTR + INSTR了不到一半的時間定期表達。

+1

只要數字總是兩位數,並且元素總是按照這個順序。不確定這個問題是否清楚。 (「類似...的東西」總是有點擔心!)。正則表達式更加靈活,但顯然可能是有代價的。 – 2014-09-10 15:12:01

+0

看看我的答案。 REGEXP戲劇性地擊敗SUBSTR + INSTR。 – zaratustra 2014-09-10 15:13:51

+0

@zaratustra,當然,讓我看看並測試它。 – 2014-09-10 15:36:34

0

我的版本,當數據長度不固定時很有用。這可能是generic

regexp_substr的最後一個參數實際上會返回相應的結果!

SELECT level as case , 
     regexp_substr('Chapter 180 Unit 10 Sect 16 World 100', '\w* \d*()*',1,level) as result 
FROM dual 
    CONNECT BY level <= CEIL(regexp_count('Chapter 180 Unit 10 Sect 16 World 100',' ')/2) 

結果:

 CASE RESULT 
---------- ------------------------ 
     1 Chapter 180 
     2 Unit 10 
     3 Sect 16 
     4 World 100 

Fiddle Demo

+0

從性能角度來看,REGEXP將會非常慢。 – 2014-09-11 09:22:39

+0

他們可能不是OLTP交易友好的,但非常靈活! – 2014-09-11 10:07:56

+0

靈活,是的。資源飢餓,是的:-) – 2014-09-11 10:08:50