我有一个这样的表(例子):
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 :(得分: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)条目。