示例:
这是员工表:
CREATE TABLE `employees` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`code` varchar(4) NOT NULL,
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
);
code
是4个字符的简单登录代码。使用deleted_at
字段可实现软删除。现有员工是拥有deleted_at=NULL
的员工。
我们需要使当前员工之间的代码保持唯一。
在code
字段上使用UNIQUE约束将防止当前员工使用软删除的员工使用的代码。
如何强制执行此约束?
这是有关如何在MySQL中强制执行一致性约束的一般问题的示例。
编辑:
可以按照@ bill-karwin的建议更改架构以利用唯一约束。
如何应用可能跨越多个表的复杂一致性约束?
答案 0 :(得分:1)
一种相对简单的解决方案是将deleted_at
列的默认值更改为NULL
以外的其他值(例如'1900-01-01',甚至是“零”日期' 0000-00-00”(如果您启用了它们)。然后,您可以在UNIQUE
上创建一个(code, deleted_at)
索引,这将防止任何员工使用当前员工拥有的代码(因为您将在(code,default)
上找到匹配项),但不能排除它们使用默认密码,因为默认值与deleted_at
时间戳不匹配。
答案 1 :(得分:0)
一种解决方案是创建一个可空列is_active
,该列限制为NULL或单个非NULL值。 code
和is_active
列必须唯一。
CREATE TABLE `employees` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`code` varchar(4) NOT NULL,
`is_active` enum('yes'),
`deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY (`code`, `is_active`)
);
如果is_active
为NULL,则它允许code
列中任意数量的重复项。如果is_active
不为NULL,则它仅允许一个值“是”,因此code
列中的每个值都必须是唯一的。
deleted_at
不再指示员工当前是否处于活动状态,仅指示何时被停用。
发表评论:
在SQL标准中,跨越多个表的约束称为ASSERTIONS
,但是实际上没有RDBMS产品实现该标准中的功能。
一些使用触发器实现约束的方法,但是如何设计触发器以有效地完成您想要的事情并不总是显而易见的。
老实说,大多数人出于这种约束而求助于应用程序逻辑。这会带来比赛条件的风险。一旦执行SELECT语句来验证数据满足约束,其他并发会话可能会提交破坏约束的数据,然后会话才能提交更改。
唯一的解决方案是使用悲观锁定,以确保没有其他会话可以跳到您前面。