2012-10-09 64 views
3

我想在PostgreSQL 9.0.1中做一個稍微複雜的字符串轉換。在my_col的值是在的格式長串:CASE與正則表達式:「在上下文中調用的集值函數,不能接受集」

'12345_sometext_X12B_1' 
'12345_sometext_optionaltext_Y09B_1' 
'12345_sometext_optionaltext_X12A_1' 

我需要將「X12」部分轉化爲具有已知的數值,有幾個不同的已知的值(最多5)。

我希望能夠在不需要子查詢的情況下在一個查詢中確定這一點。但是,以下內容不適合我。最後一列是拋出異常的那一列。看起來我不能用CASE語句使用這些函數的輸出組合出於某種原因。我只列出了進行中的列,僅用於演示目的。

select 
      regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'), -- returns {'X12'} 
     (regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1], -- returns 'X12' 
    case (regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1] 
     when 'X12' then '1200' 
     when 'Y09' then '950' 
     else '?' end -- should return '1200' but throws error 
from my_table; 

相反,我得到的錯誤:

ERROR: set-valued function called in context that cannot accept a set 
SQL state: 0A000 

有人可以告訴我?

+0

第一:更新,你在一個過時的版本。升級到9.0.10以改進錯誤修復,安全性和穩定性。不需要轉儲和重新加載,只需安裝新的二進制文件即可。 –

回答

5

鑑於數據:

create table my_table(my_col text); 
insert into my_table(my_col) values 
('12345_sometext_X12B_1'), 
('12345_sometext_optionaltext_Y09B_1'), 
('12345_sometext_optionaltext_X12A_1'), 
('nomatch'); 

上面的查詢會產生你報告錯誤。很奇怪,因爲:

SELECT pg_typeof((regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$'))[1]); 

回報 '文本'。它應該真的說setof text雖然,那就是陷阱:regex_matches是一個集合返回函數。那些在PostgreSQL的FROM子句之外調用時會有......有趣的行爲。

pattern matching

The regexp_matches function returns a text array of all of the captured substrings resulting from matching a POSIX regular expression pattern. It has the syntax regexp_matches(string, pattern [, flags ]). The function can return no rows, one row, or multiple rows

請重新定義查詢使用子查詢調用SRF。如果多行被匹配返回此會失敗,但:

SELECT 
    CASE (SELECT x[1] FROM regexp_matches(my_col, E'^.*_([^_]*)[A-Z]{1}_\\d*$') x) 
    WHEN 'X12' THEN '1200' 
    WHEN 'Y09' THEN '950' 
    ELSE '?' 
    END 
FROM my_table; 

想看SRF用於如何怪異的SELECT是PG?比較這些查詢的結果:

SELECT generate_series(1,10), generate_series(1,15); 

和:

SELECT generate_series(1,10), generate_series(1,20); 

第一屆產生30行。第二個產生20.解釋爲什麼。如果偶爾有用的結果,Pg中SELECT列表中的多個SRF會產生瘋狂的結果。

感謝Tom Lane,PostgreSQL 9.3支持SQL標準LATERAL子句,它爲當前行爲提供了一個明智而明確的替代方案。

+0

通過查看行數改變,遞增地將參數更改爲'generate_series()'是令人震驚的。 – cpburnz

+0

@cpburnz ...和它的工作原理完全無意義的方式?是的。這就是爲什麼我使用'LATERAL',從不在'SELECT'列表中設置返回函數的原因。 –

1

regexp_matches()返回SETOF text[](一組文本陣列的),它是用於在相同串的一個圖案的多個匹配是有用的。但是對於這個任務,這只是錯誤的工具。使用substring()代替正則表達式。它返回text。重用demo table in @Craig's answer

SELECT CASE substring(my_col, '^.*_([^_]*)[A-Z]_\d*$') 
     WHEN 'X12' THEN '1200' 
     WHEN 'Y09' THEN '950' 
     ELSE   '?' 
     END As result 
FROM my_table; 

返回:

result 
-------- 
1200 
950 
1200 
? 

也略有簡化了正則表達式。 {1}只是噪音。

如果你需要優化性能儘量做沒有正則表達式,這是強大的,但相對昂貴。例如:

reverse(right(split_part(reverse(my_col), '_', 2), -1)) 

看起來更復雜,但在我的測試中仍然更快。

+0

無論出於什麼原因,子串在這裏的性能都差很多(儘管它解決了我特別的問題,這與上面的問題相同)。 你的答案缺少,問題包括數組索引器:'regexp_matches(src,regex)[1]'。 –

+0

@RhysvanderWaerden:這個理由很有趣,我不能重現它。 substring()'在我的測試中更快(正如預期的那樣)。爲什麼答案會丟失關於數組索引的任何內容?無論使用或不使用數組索引,SRF都無效。我更新了一點,並添加了更快的選擇。 –