MySQL suporta funcional peças-chave desde 8.0.13 .
-
Se sua versão for suficientemente recente, você pode definir seu índice como:
UNIQUE(`user_id`, `test_id`, (IFNULL(`completed_date`, -1)))
(Demonstração em dbfiddle.uk )
Observe que o índice acima também evitará datas duplicadas para execuções concluídas. Se isso for válido, um índice ligeiramente modificado funcionaria:
UNIQUE(`user_id`, `test_id`, ( CASE WHEN `completed_date` IS NOT NULL THEN NULL ELSE 0 END))
(Demonstração em dbfiddle.uk )
Embora então comece a parecer um pouco sujo;)
-
Se você tiver pelo menos a versão 5.7 você pode usar uma coluna (virtual) como solução alternativa:
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`) );
(Demonstração em dbfiddle.uk )
-
Se você está preso em 5.6 em seguida, uma combinação de uma coluna regular (não virtual) eINSERT
ligeiramente modificada declarações funcionariam:
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`) );
Neste caso, você definiriais_open
paratrue
para execuções incompletas e paraNULL
após a conclusão, fazendo uso do fato de que doisNULL
s são tratados como não iguais.
(Demonstração em dbfiddle.uk )