2011-08-29 53 views
-1

我有这样(实施例)的表:确保对表原子更新与特定的逻辑约束

id  INT  PRIMARY KEY 
number BIGINT UNIQUE 
type  ENUM 
user_id INT 

number是该表的主要数据,表示其中只有一个可以存在一个唯一的编号。这些数字可以是type A或B.用户可以声明这些数字,在这种情况下,user_id将被填入(直到此时为NULL)。

在这个具体的逻辑约束是,许多只能使用一次权利和用户只能权利要求一个数A型的,但类型B的无限数量

为了确保用户可以只要求A类型的一个数字,那么UNIQUE (type, user_id)约束就可以。这将阻止用户声称无限数量的B号码。

目前,我在应用层这样处理的:

SELECT COUNT(*) FROM `table` WHERE `type` = 'A' AND `user_id` = ? 

如果计数不为0,中止,否则:

UPDATE `table` SET `user_id` = ? WHERE `type` = 'A' AND `user_id` IS NULL LIMIT 1 

但还是有一个微小的机会在这种情况下,用户会得到两个A型号码。

如何制定约束或原子更新,以确保用户只能声明一个A型号码?在这里存储过程,触发器和类似的帮助吗?在没有重构架构的情况下,这可能在MySQL中吗?

+0

何时知道1327号是A型还是B型? –

+0

@Catcall它们是这样生成的。例如。 'INSERT INTO table(number,type)VALUES(1234,'A'),(2345,'B'),...'。 – deceze

回答

0

只需使用SELECT ... **FOR UPDATE**

SELECT * FROM `table` WHERE `type` = 'A' AND `user_id` = ? FOR UPDATE 

不知道它是否适合COUNT(*),但它很容易检查。

如果可以添加另一列 - 那么您可以添加类似if_type_A并使用触发器进行维护:如果type为A - 则为1,否则为null。并应用user_id + if_type_A复合键的唯一约束

+0

这给了我一个数字并锁定了那一行。我不明白它是如何给我一个*无人认领的号码,并确保同时用户不会声明*其他号码。除非我错过了什么? – deceze

+0

@deceze:'SELECT .. FOR UPDATE'将解决你的竞争条件问题。这不是你想要的吗? – zerkms

+0

您的第二个建议不会归结为无法为同一用户分配多个'if_type_A'的问题吗? – deceze

0

稍有不同的方法。使用一个表来记录数字和它们的类型,一个表记录类型'a'的声称数字,一个表记录类型'b'的声明数字。类型'a'和类型'b'数字具有不同的逻辑约束;将它们存储在不同的表格中非常有意义。

create table unique_numbers (
    n integer primary key, 
    n_type char(1) default 'b' check (n_type in ('a', 'b')), 
    unique (n, n_type) 
); 

create table claimed_type_a_numbers (
    n integer primary key, 
    n_type char(1) not null default 'a' check (n_type = 'a'), 
    user_id integer not null unique, -- references users, not shown 
    foreign key (n, n_type) references unique_numbers (n, n_type) 
); 

create table claimed_type_b_numbers (
    n integer primary key, 
    n_type char(1) not null default 'b' check (n_type = 'b'), 
    user_id integer not null, -- references users, not shown 
    foreign key (n, n_type) references unique_numbers (n, n_type) 
); 

但MySQL不支持CHECK约束。 (“The CHECK clause is parsed but ignored by all storage engines.”)所以你可能想用MySQL触发器来实现这些CHECK约束。

0

如果您的ENUM的架构允许空值,你不会有重组的模式,但应用程序将需要此解决方案的更新:

使用null表示“B”型数字。然后你可以在(type,user_id)上设置一个唯一的约束。 MySQL将允许索引中的多个(NULL,user_id)条目。