sql >> Base de Datos >  >> RDS >> Mysql

Establecer una restricción única solo cuando un campo es nulo

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

    (Demostración en dbfiddle.uk )

  • 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 a true para ejecuciones incompletas y para NULL después de la finalización, haciendo uso del hecho de que dos NULL s son tratados como no iguales.

    (Demostración en dbfiddle.uk )