MySQL obsługuje funkcjonalne kluczowe części od 8.0.13 .
-
Jeśli Twoja wersja jest wystarczająco aktualna, możesz zdefiniować swój indeks jako:
UNIQUE(`user_id`, `test_id`, (IFNULL(`completed_date`, -1)))
Należy pamiętać, że powyższy indeks zapobiegnie również powielaniu dat ukończonych egzekucji. Jeśli te powinny być prawidłowe, zadziałałby nieznacznie zmodyfikowany indeks:
UNIQUE(`user_id`, `test_id`, ( CASE WHEN `completed_date` IS NOT NULL THEN NULL ELSE 0 END))
Chociaż wtedy zaczyna się czuć trochę brudno;)
-
Jeśli masz co najmniej wersję 5.7 możesz użyć (wirtualnej) wygenerowanej kolumny jako obejście:
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`) );
-
Jeśli utkniesz na 5.6 następnie połączenie zwykłej (niewirtualnej) kolumny i nieznacznie zmodyfikowanej
INSERT
oświadczenia zadziałałyby: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`) );
W takim przypadku ustaw
is_open
natrue
dla niekompletnych wykonań i doNULL
po zakończeniu, korzystając z faktu, że dwaNULL
s są traktowane jako nierówne.