确保具有特定逻辑约束的表的原子更新

时间:2011-08-29 02:16:36

标签: mysql

我有一个这样的表(例子):

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中是否可行?

3 个答案:

答案 0 :(得分: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复合键

应用唯一约束

答案 1 :(得分: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约束。

答案 2 :(得分:0)

如果您的ENUM模式允许空值,则不必重新构建模式,但应用程序将需要更新此解决方案:

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