2014-01-06 65 views
0

不幸的是,我有類似下面的表格:法從非標準標記字符串值的PostgreSQL

DROP TABLE IF EXISTS my_list; 
CREATE TABLE my_list (index int PRIMARY KEY, mystring text, status text); 

INSERT INTO my_list  
(index, mystring,           status) VALUES 
    (12, '',             'D'), 
    (14, '[id] 5',            'A'), 
    (15, '[id] 12[num] 03952145815',       'C'), 
    (16, '[id] 314[num] 03952145815[name] Sweet',    'E'), 
    (19, '[id] 01211[num] 03952145815[name] Home[oth] Alabama', 'B'); 

有什麼訣竅從上面顯示的文本mystring走出[id]數量integer?彷彿我跑下面的查詢:

SELECT index, extract_id_function(mystring), status FROM my_list; 

,並得到了類似的結果:

12 0  D 
14 5  A 
15 12 C 
16 314 E 
19 1211 B 

優選僅簡單的字符串函數,如果沒有正則表達式將被罰款。

+1

**總是在問題**中指定PostgreSQL版本。 (請在完成後進行編輯和評論)。然後請找出設計該架構的人並向他們說明意思;-)。無論如何,我會給出答案。 –

+0

另外,*爲什麼*你想避免正則表達式?有時他們是工作的正確工具。尤其是考慮到SQL中的字符串操作有多痛苦,因爲無法在同一查詢級別的其他位置輕鬆引用值。 –

+0

我在Windows 7上的實際版本是9.1。我使用正則表達式對查詢進行了一些測試,然後發現該正則表達式在unicode字母方面存在問題,而這些字母常常是我的語言,所以我不能使用它可靠。很明顯,我做出了這個shema,我準備好爲自己說:)但是今天我絕對不會那麼做。用於我使用的編程。NET這樣的表達式不是問題,但我對PostgreSQL的想法不夠。 –

回答

2

如果我理解正確,你有一個非常規的標記格式,其中[id]後跟一個空格,然後是一系列代表數字標識符的數字。沒有結束標記,下一個非數字字段結束標識。

如果是這樣,你將能夠用non-regexp字符串操作來做到這一點,但只能非常糟糕。你真正需要的是相當於strtol的SQL,它消耗了直到第一個非數字的輸入,並且只返回它。轉換爲integer不會這樣做,如果在數字後面看到非數字垃圾,它會報告錯誤。 (正如我剛剛寫了一個C擴展,揭露strtol解碼十六進制值,但我猜你不想使用C擴展,如果你甚至不想正則表達式......)

它可以與字符串OPS做,如果你做了簡化假設,即[id] nnnn標籤總是用繩子或另一個標籤年底結束,所以它總是[在號碼的末尾。如果多個字符串出現,我們還假定您只對第一個[id]感興趣。這樣,你可以寫類似下面的恐怖怪物:

select 
    "index", 
    case 
    when next_tag_idx > 0 then substring(cut_id from 0 for next_tag_idx) 
    else cut_id 
    end AS "my_id", 
    "status" 
from (
    select 
    position('[' in cut_id) AS next_tag_idx, 
    * 
    from (
    select 
     case 
     when id_offset = 0 then null 
     else substring(mystring from id_offset + 4) 
     end AS cut_id, 
     * 
    from (
     select 
     position('[id] ' in mystring) AS id_offset, 
     * 
     from my_list 
    ) x 
) y 
) z; 

如果有人曾經實際使用該查詢什麼,小貓會從天上掉下來,並圖示在人行道上,驚恐地哀號一路下跌)。

或者你也可以是明智的,只是使用正則表達式對這種字符串處理的,在這種情況下,您的查詢(假設你只想要第一個[id])是:

regress=> SELECT 
      "index", 
      coalesce((SELECT (regexp_matches(mystring, '\[id\]\s?(\d+)'))[1])::integer, 0) AS my_id, 
      status 
      FROM my_list; 
index | my_id   | status 
-------+----------------+-------- 
    12 | 0    | D 
    14 | 5    | A 
    15 | 12    | C 
    16 | 314   | E 
    19 | 01211   | B 
(5 rows) 

更新:如果你在使用正則表達式處理unicode問題時,升級到Pg 9.2。請參閱https://stackoverflow.com/a/14293924/398670

+0

你好Craig並且感謝你對範例的廣泛解釋。這真的是我應該考慮創建一個腳本來改變過去數據的地方!由於這兩種解決方案都很複雜且耗時。無論如何,正則表達式看起來更容易接受,也更不容易出錯。您的表情是否可以升級到索引12(第一行)上的數字0? –

+0

@ user973238當然。這是一個簡單的'coalesce'。是的,我建議將數據分解到您的模式中,這樣您就不必一直進行這種處理。如果你試圖存儲鍵/值數據(標籤等),如果沒有固定的屬性名稱列表,你可以用作列,查看'hstore',或者考慮存儲'json'字段。或者如果卡住了,你可以回退EAV。 –

+0

hstore非常有趣的事情,不知道這一點。我雖然關於存儲XML這樣的東西,但我看到有提供的方法。謝謝克雷格。 –