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

Mysql 确保对具有特定逻辑约束的表进行原子更新,mysql,Mysql,我有这样一张表(示例): number是此表的主要数据,表示唯一的编号,其中只能存在一个编号。这些数字可以是类型A或B。用户可以申请这些数字,在这种情况下,将填写用户id(直到那时它是空的) 具体的逻辑限制是,一个数字只能声明一次,用户只能声明一个a型数字,但不限制B型数字 为了确保用户只能声明一个类型a的数字,可以使用UNIQUE(type,user\u id)约束。但这将阻止用户申请无限数量的B号码 目前,我在应用程序级别处理此问题,如下所示: SELECT COUNT(*) FROM `t

我有这样一张表(示例):

number
是此表的主要数据,表示唯一的编号,其中只能存在一个编号。这些数字可以是
类型
A或B。用户可以申请这些数字,在这种情况下,将填写
用户id
(直到那时它是
空的

具体的逻辑限制是,一个数字只能声明一次,用户只能声明一个a型数字,但不限制B型数字

为了确保用户只能声明一个类型a的数字,可以使用
UNIQUE(type,user\u 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中,不重新构造模式就可以做到这一点吗?

只需使用
选择**更新**

SELECT * FROM `table` WHERE `type` = 'A' AND `user_id` = ? FOR UPDATE
不确定它是否适用于
COUNT(*)
,但很容易检查


如果可以添加另一列,那么您可以添加类似于
If\u type\u A
的内容,并使用触发器对其进行维护:If type是A-then 1,else-null。如果复合键的方法稍有不同,则为用户id+应用唯一约束。使用一个表格记录编号及其类型,一个表格记录“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不支持检查约束。(“”)因此,您可能希望使用MySQL触发器实现这些检查约束。

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


使用null表示“B”类型的数字。然后可以对(类型、用户id)设置唯一约束。MySQL将允许索引中有多个(NULL,user_id)条目。

这会给我一个数字并锁定该行。我不明白它是如何给我一个无人认领的号码,同时确保同一用户没有认领其他号码。除非我遗漏了什么?@deceze:
SELECT。。更新
将解决您的比赛条件问题。这难道不是你想要的吗?你的第二个建议是否可以归结为同一个问题:如果为同一个用户键入A,那么不能分配多个
。正如我所说,我不明白它是如何解决我的竞争条件问题的。我可以
选择。。。对于更新
a行
,其中type='a'和user_id为NULL
,但这并不能阻止我在并行会话中一次又一次地进行更新,是吗?什么时候知道1327是type a还是type B?@Catcall它们是这样生成的。例如,
将值(1234,'A')、(2345,'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)
);