PostgreSQL: contrainte conditionnellement unique

116

Je voudrais ajouter une contrainte qui applique l'unicité sur une colonne uniquement dans une partie d'une table.

ALTER TABLE stop ADD CONSTRAINT myc UNIQUE (col_a) WHERE (col_b is null);

La WHEREpartie ci-dessus est un vœu pieux.

Un moyen de faire ça? Ou devrais-je retourner à la planche à dessin relationnelle?

EoghanM
la source
2
Généralement fait. Voir "index unique partiel"
Craig Ringer
11
@yvesonline non, c'est une contrainte unique régulière. L'affiche veut une contrainte unique partielle .
Craig Ringer

Réponses:

186

PostgreSQL ne définit pas de UNIQUEcontrainte partielle (c'est-à-dire conditionnelle) - cependant, vous pouvez créer un index unique partiel . PostgreSQL utilise des index uniques pour implémenter des contraintes uniques, donc l'effet est le même, vous ne verrez tout simplement pas la contrainte répertoriée dans information_schema.

CREATE UNIQUE INDEX stop_myc ON stop (col_a) WHERE (col_b is NOT null);

Voir les index partiels .

Craig Ringer
la source
24
Super! Il n'est pas intuitif que la "contrainte" n'apparaisse pas comme une contrainte, mais donne néanmoins l'erreur souhaitée deERROR: duplicate key value violates unique constraint "stop_myc"
EoghanM
7
Il convient de noter que cela ne permettra pas de créer une référence FK dans ce champ partiellement unique.
ffflabs
11
Il convient également de noter que ces effets d'indice ne peuvent pas être différés. Si vous devez effectuer des mises à jour en masse, cela peut poser un problème car l'unicité est vérifiée après chaque ligne, pas après l'instruction comme ce serait pour une contrainte ou après la transaction comme ce serait pour une contrainte reportable.
sage88
37

il a déjà été dit que PG ne définit pas une contrainte UNIQUE partielle (ie conditionnelle). La documentation indique également que la meilleure façon d'ajouter une contrainte unique à une table est les ADD CONSTRAINT index uniques

La méthode préférée pour ajouter une contrainte unique à une table est ALTER TABLE ... ADD CONSTRAINT. L'utilisation d'index pour appliquer des contraintes uniques peut être considérée comme un détail d'implémentation auquel il ne faut pas accéder directement. Il faut cependant savoir qu'il n'est pas nécessaire de créer manuellement des index sur des colonnes uniques; cela ne ferait que dupliquer l'index créé automatiquement.

Il existe un moyen de l'implémenter en utilisant des contraintes d'exclusion , (merci @dukelion pour cette solution)

Dans votre cas, cela ressemblera à

ALTER TABLE stop ADD CONSTRAINT myc EXCLUDE (col_a WITH =) WHERE (col_b IS null);
Peter Yeremenko
la source
sur cette approche, vous n'utilisez pas "using" pour définir la méthode d'index, donc cela peut être extrêmement lent ou postgres crée un index par défaut sur cela? Cette méthode est le choix canonique, mais pas toujours le meilleur choix! Je pense que vous aurez besoin d'une clause "using" avec index pour que ce choix soit le meilleur.
Natan Medeiros
10
Bien que plus lente, l'avantage de la solution d'exclusion est qu'elle est reportable (et par défaut diffère jusqu'à la fin de l'instruction). En revanche, la solution d'index unique acceptée ne peut pas être différée (et est vérifiée après chaque changement de ligne). Ainsi, une mise à jour en masse n'est souvent pas possible car les étapes de la mise à jour violeraient la contrainte unique, même si elle ne serait pas violée à la fin de l'instruction de mise à jour atomique.
sage88
1
Cette note a été supprimée de la documentation en août 2015
Raniz