仅当字段为null时设置唯一约束

时间:2019-05-11 19:38:12

标签: mysql constraints unique-constraint mysql-5.6

我有这张桌子:

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_idtest_id设置唯一约束,但仅当conclusion_date为空时。如果conclusion_date不为null,则约束不适用。

因此,每个用户和测试只会存在一个不完整的执行。

类似这样的东西:

UNIQUE(`user_id`, `test_id`) WHEN (`completed_date` IS NULL)

如何在MySQL 5.6上完成此操作?

1 个答案:

答案 0 :(得分:1)

MySQL从 8.0.13 开始支持functional key parts

  • 如果您的版本足够新,则可以将索引定义为:

    UNIQUE(`user_id`, `test_id`, (IFNULL(`completed_date`, -1)))
    

    Demo on dbfiddle.uk

    请注意,以上索引还将防止重复执行完成的日期。如果这些参数有效,那么略微修改的索引将起作用:

    UNIQUE(`user_id`, `test_id`, (
        CASE WHEN `completed_date` IS NOT NULL
        THEN NULL
        ELSE 0
    END))
    

    Demo on dbfiddle.uk

    尽管那样会开始有点脏;)

  • 如果您的版本至少为 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`)
    );
    

    Demo on dbfiddle.uk

  • 如果您被困在 5.6 上,则可以使用常规(非虚拟)列和稍作修改的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,这是因为将两个li $v0, 4 la $a0, msg1 syscall li $v0, 6 syscall s.s $f0, $f1 li $v0, 4 la $a0, msg2 syscall li $v0, 6 syscall s.s $f0, $f2 li $v0, 2 div.s $f12, num1, num2 syscall 视为不执行相等。

    ({Demo on dbfiddle.uk