例:
这是员工表:
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建议的唯一约束。
那么应用可能跨越多个表的复杂一致性约束呢?
解决问题的一个相对简单的解决方案是将deleted_at
列更改为NULL
以外的其他值(例如'1900-01-01',或者如果启用它们,甚至是“零”日期'0000-00-00' )。然后,您可以在UNIQUE
上创建一个(code, deleted_at)
索引,这将阻止任何员工使用当前员工的代码(因为您将获得(code,default)
上的匹配),但不能使用之前员工使用过的代码将其排除,因为默认值与deleted_at
时间戳不匹配。
一种解决方案是创建一个可为空的列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,则它只允许一个值'yes',因此code
列中的每个值必须是唯一的。
deleted_at
不再指示员工当前是否处于活动状态,只有在此时才被停用。
你的评论:
跨越多个表的约束在SQL标准中称为ASSERTIONS
,但实际上没有RDBMS产品从标准中实现该功能。
有些人使用触发器实现约束,但是如何设计触发器来有效地执行您想要的操作并不总是显而易见的。
老实说,大多数人都会使用应用程序逻辑来处理这些类型的约束。这带来了一些竞争条件的风险。只要执行SELECT语句以验证数据是否满足约束,其他一些并发会话就可以在会话提交更改之前提交破坏约束的数据。
唯一的解决方案是使用悲观锁定来确保没有其他会话可以跳到你前面。