2015-01-20 93 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 
相关问题