Устанавливать уникальное ограничение только в том случае, если поле пустое - PullRequest
0 голосов
/ 11 мая 2019

У меня есть эта таблица:

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?

1 Ответ

1 голос
/ 11 мая 2019

MySQL поддерживает функциональные ключевые части , начиная с 8.0.13 .

  • Если ваша версия достаточно свежая, вы можете определить свой индекс как:

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

    ( Демонстрация на dbfiddle.uk )

    Обратите внимание, что вышеуказанный индекс также предотвратит дублирование дат для завершенных выполнений.Если они должны быть действительными, то будет работать слегка измененный индекс:

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

    ( Демонстрация на dbfiddle.uk )

    Хотя тогда он начинает казаться немного грязным;)

  • Если у вас есть хотя бы версия 5.7 , вы можете использовать (виртуальный) сгенерированный столбец в качестве обходного пути:

    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`)
    );
    

    ( Демонстрация на 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 считаются не равными.

    ( Демонстрация на dbfiddle.uk )

...