Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Legen Sie eine Eindeutigkeitsbeschränkung nur fest, wenn ein Feld null ist

MySQL unterstützt funktionale Schlüsselteile seit 8.0.13 .

  • Wenn Ihre Version aktuell genug ist, können Sie Ihren Index wie folgt definieren:

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

    (Demo auf dbfiddle.uk )

    Beachten Sie, dass der obige Index auch doppelte Daten für abgeschlossene Ausführungen verhindert. Wenn diese gültig sein sollten, würde ein leicht modifizierter Index funktionieren:

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

    (Demo auf dbfiddle.uk )

    Allerdings fühlt es sich dann etwas schmutzig an;)

  • Wenn Sie mindestens Version 5.7 haben Sie können eine (virtuelle) generierte Spalte verwenden als Problemumgehung:

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

    (Demo auf dbfiddle.uk )

  • Wenn Sie bei 5.6 hängen bleiben dann eine Kombination aus einer regulären (nicht virtuellen) Spalte und leicht modifiziertem INSERT Anweisungen funktionieren würden:

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

    In diesem Fall würden Sie is_open setzen auf true für unvollständige Ausführungen und auf NULL nach Abschluss, wobei die Tatsache ausgenutzt wird, dass zwei NULL s werden als ungleich behandelt.

    (Demo auf dbfiddle.uk )