仅当字段为空时设置唯一约束
Set a unique constraint only when a field is null
我有这个table:
CREATE TABLE `executed_tests` (
`id` INTEGER AUTO_INCREMENT NOT NULL,
`user_id` INTEGER NOT NULL,
`test_id` INTEGER NOT NULL,
`start_date` DATE NOT NULL,
`completed_date` DATE,
PRIMARY KEY (`id`)
);
我想在字段 user_id
和 test_id
上设置唯一约束,但仅当 conclusion_date
为空时。如果 conclusion_date
不为空,则约束不适用。
因此每个用户和测试只存在一次不完整的执行。
像这样:
UNIQUE(`user_id`, `test_id`) WHEN (`completed_date` IS NULL)
如何在 MySQL 5.6 上完成此操作?
MySQL 支持 functional key parts 自 8.0.13.
如果您的版本足够新,您可以将索引定义为:
UNIQUE(`user_id`, `test_id`, (IFNULL(`completed_date`, -1)))
请注意,上述索引还将防止已完成执行的日期重复。如果这些应该是有效的,那么稍微修改一下索引就可以了:
UNIQUE(`user_id`, `test_id`, (
CASE WHEN `completed_date` IS NOT NULL
THEN NULL
ELSE 0
END))
虽然后来开始觉得有点脏了;)
如果您的版本至少为 5.7,您可以使用(虚拟)generated column 作为解决方法:
CREATE TABLE `executed_tests` (
`id` INTEGER AUTO_INCREMENT NOT NULL,
`user_id` INTEGER NOT NULL,
`test_id` INTEGER NOT NULL,
`start_date` DATE NOT NULL,
`completed_date` DATE,
`_helper` CHAR(11) AS (IFNULL(`completed_date`, -1)),
PRIMARY KEY (`id`),
UNIQUE(`user_id`, `test_id`, `_helper`)
);
如果您卡在 5.6 上,那么结合使用常规 (non-virtual) 列和稍作修改的 INSERT
语句会工作:
CREATE TABLE `executed_tests` (
`id` INTEGER AUTO_INCREMENT NOT NULL,
`user_id` INTEGER NOT NULL,
`test_id` INTEGER NOT NULL,
`start_date` DATE NOT NULL,
`completed_date` DATE,
`is_open` BOOLEAN,
PRIMARY KEY (`id`),
UNIQUE(`user_id`, `test_id`, `is_open`)
);
在这种情况下,您可以将 is_open
设置为 true
用于不完整的执行,并在完成后设置为 NULL
,利用两个 NULL
被处理的事实不等于。
我有这个table:
CREATE TABLE `executed_tests` (
`id` INTEGER AUTO_INCREMENT NOT NULL,
`user_id` INTEGER NOT NULL,
`test_id` INTEGER NOT NULL,
`start_date` DATE NOT NULL,
`completed_date` DATE,
PRIMARY KEY (`id`)
);
我想在字段 user_id
和 test_id
上设置唯一约束,但仅当 conclusion_date
为空时。如果 conclusion_date
不为空,则约束不适用。
因此每个用户和测试只存在一次不完整的执行。
像这样:
UNIQUE(`user_id`, `test_id`) WHEN (`completed_date` IS NULL)
如何在 MySQL 5.6 上完成此操作?
MySQL 支持 functional key parts 自 8.0.13.
如果您的版本足够新,您可以将索引定义为:
UNIQUE(`user_id`, `test_id`, (IFNULL(`completed_date`, -1)))
请注意,上述索引还将防止已完成执行的日期重复。如果这些应该是有效的,那么稍微修改一下索引就可以了:
UNIQUE(`user_id`, `test_id`, ( CASE WHEN `completed_date` IS NOT NULL THEN NULL ELSE 0 END))
虽然后来开始觉得有点脏了;)
如果您的版本至少为 5.7,您可以使用(虚拟)generated column 作为解决方法:
CREATE TABLE `executed_tests` ( `id` INTEGER AUTO_INCREMENT NOT NULL, `user_id` INTEGER NOT NULL, `test_id` INTEGER NOT NULL, `start_date` DATE NOT NULL, `completed_date` DATE, `_helper` CHAR(11) AS (IFNULL(`completed_date`, -1)), PRIMARY KEY (`id`), UNIQUE(`user_id`, `test_id`, `_helper`) );
如果您卡在 5.6 上,那么结合使用常规 (non-virtual) 列和稍作修改的
INSERT
语句会工作:CREATE TABLE `executed_tests` ( `id` INTEGER AUTO_INCREMENT NOT NULL, `user_id` INTEGER NOT NULL, `test_id` INTEGER NOT NULL, `start_date` DATE NOT NULL, `completed_date` DATE, `is_open` BOOLEAN, PRIMARY KEY (`id`), UNIQUE(`user_id`, `test_id`, `is_open`) );
在这种情况下,您可以将
is_open
设置为true
用于不完整的执行,并在完成后设置为NULL
,利用两个NULL
被处理的事实不等于。