2015-01-20 123 views
0

假設我有以下結構的表:正確的方式來實現子ID?

CREATE TABLE test(
    id SERIAL, 
    type VARCHAR(10), 
    sub_id INTEGER, 
    UNIQUE (type, sub_id) 
) 

,我希望「sub_id」欄目是一個特定的「類型」中的計數器。例如:

id | type | sub_id 
--------------------- 
1 | 'foo' | 1 
-------------------- 
2 | 'foo' | 2 
-------------------- 
3 | 'foo' | 3 
-------------------- 
4 | 'bar' | 1 
-------------------- 
5 | 'bar' | 2 

要插入新行我想用下面的查詢:

INSERT INTO test(type,sub_id) 
SELECT 'foo', MAX(sub_id)+1 
FROM test 
WHERE type='foo' 

但後來我發現,這種查詢是脆弱的競爭條件。 將子計數器保存在類型中的正確方法是什麼?

+0

真的,有可能不涉及表鎖不正確的方式,你可以得到的東西這幾乎可以通過使用序列來工作,但你已經有了幾乎可以工作的東西。行被刪除時應該發生什麼? – Jasen 2015-01-21 00:44:02

回答

0

如果可能的類型的數量是預先知道的,我會建議使用專用序列:

CREATE SEQUENCE foo_sub_id; 
CREATE SEQUENCE bar_sub_id; 

我不知道您的實際需求,但另一種選擇是,當然不是實際存儲在sub_id,但來計算的話,無論你需要消耗它:

SELECT id, type, row_number() OVER (PARTITION BY type ORDER BY id) sub_id 
FROM test 
0

如果它是必須有在表中sub_ids和順序編號而沒有間隙,然後用序列並不打算這樣做,
,因爲序列可能有空位。所以,你將不得不鎖定整個表格,然後使用原來的方法。

BEGIN Transaction; 
LOCK TABLE test; 
INSERT INTO test(type,sub_id) 
SELECT 'foo', MAX(sub_id)+1 
FROM test 
WHERE type='foo'; 
COMMIT; 

如果允許間隙,只需使用id列。

如果它只是「看起來」由盧卡斯埃德爾提出的窗口查詢看起來不錯,對我來說太:

SELECT id, type, 
    row_number() OVER (PARTITION BY type ORDER BY id) AS sub_id 
FROM test 
相關問題