MySQL admite funcional partes clave desde 8.0.13 .
-
Si su versión es lo suficientemente reciente, puede definir su índice como:
UNIQUE(`user_id`, `test_id`, (IFNULL(`completed_date`, -1)))
(Demostración en dbfiddle.uk )
Tenga en cuenta que el índice anterior también evitará fechas duplicadas para ejecuciones completadas. Si esos fueran válidos, entonces funcionaría un índice ligeramente modificado:
UNIQUE(`user_id`, `test_id`, ( CASE WHEN `completed_date` IS NOT NULL THEN NULL ELSE 0 END))
(Demostración en dbfiddle.uk )
Aunque luego empieza a sentirse un poco sucio;)
-
Si tiene al menos la versión 5.7 puede usar una columna generada (virtual) como solución 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`) );
-
Si está atascado en 5.6 luego una combinación de una columna normal (no virtual) y
INSERT
ligeramente modificado las declaraciones funcionarían: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`) );
En este caso, establecería
is_open
atrue
para ejecuciones incompletas y paraNULL
después de la finalización, haciendo uso del hecho de que dosNULL
s son tratados como no iguales.