2017-08-30 1 views
2

J'ai le tableau suivant:Est-il possible d'utiliser IS NOT DISTINCT FROM dans une contrainte d'exclusion?

CREATE TABLE claim (
    claim_number TEXT NOT NULL, 
    line_id TEXT, 
    process TEXT NOT NULL 
); 

Je veux ajouter une contrainte pour que la combinaison de claim_number et line_id est toujours unique.

Dans la plupart des cas, ligne_id est null et il n'y aura qu'une seule ligne pour ce numéro de revendication. Dans certains cas, il y aura plusieurs lignes pour un numéro de revendication donné et dans ces cas, ligne_id contiendra toujours une valeur. Le but ici est de pouvoir avoir une contrainte qui force une combinaison unique sur le combo (claim_number, line_id) afin que je puisse l'utiliser comme cible de conflit dans une instruction INSERT...ON CONFLICT DO UPDATE afin que la colonne de processus puisse être mise à jour. Une contrainte UNIQUE ne fonctionnera pas car elle n'évalue pas NULL = NULL, ce qui est logique, mais ce n'est pas ce dont j'ai besoin.

J'ai essayé d'ajouter une contrainte d'exclusion tels que:

ALTER TABLE claim 
ADD EXCLUDE (claim_number WITH =, 
       line_id WITH IS NOT DISTINCT FROM); 

Mais cela ne fonctionne pas avec:

ERROR: syntax error at or near "IS" 

Est-il possible d'utiliser IS NOT DISTINCT FROM dans une contrainte d'exclusion?

+3

Whyis line_id NULLABLE? Pourquoi est-ce le texte? (il semble être un nombre) – wildplasser

+1

Une solution de contournement simple serait de définir une valeur par défaut qui serait votre équivalent à une valeur NULL que vous géreriez bien sûr dans votre application. Je me demande aussi pourquoi ces colonnes sont TEXTE ... –

+0

@ wildplasser Je mets tous les IDs en TEXTE, toujours, de sorte que les valeurs qui ont des zéros en tête ne soient pas modifiées. Par exemple, un SSN tel que 009543219 ne doit pas être stocké comme INTEGER car il perdra des zéros en tête. De plus, les données de claim_number sont alphanumériques. Fondamentalement, si je ne fais pas de maths, ça ne devrait pas être un nombre. –

Répondre

0

Dans MSSQL Server, cela est effectué à l'aide d'un Filtered Unique Index, où le prédicat de filtre est qu'il indexe uniquement les lignes avec des valeurs non NULL.

Je ne suis pas un expert de PostgreSQL, mais googler montre qu'il est possible à l'aide d'un "index partiel": https://www.postgresql.org/docs/current/static/indexes-partial.html

CREATE UNIQUE INDEX ix_claim_lines ON claim (claim_number, line_id) 
    WHERE line_id IS NOT NULL 
+0

Je ne suis pas sûr que cela fonctionne pour cette instance car j'ai besoin de l'index en question pour couvrir toute la table, pas seulement une partie, pour que cela fonctionne au besoin avec l'instruction 'ON CONFLICT DO UPDATE'. –

+0

@GregoryArenius Cet index couvre l'ensemble du tableau.Cet index n'est pas utilisé pour la recherche ou la performance de la requête, uniquement pour appliquer la contrainte 'UNIQUE' pour' claim_number' + 'line_id' quand' line_id' est spécifié. Peut-être y a-t-il une meilleure approche que d'utiliser «ON CONFLICT»? – Dai

+0

@GregoryArenius: l'index partiel empêcherait ('claim_1',' line_1') d'être inséré une seconde fois. Mais l'inconvénient est qu'un index partiel (comme une contrainte d'exclusion) ne peut pas être utilisé pour une clause 'on conflict'. Et vous ne pouvez pas créer un _constraint_ unique en utilisant un index partiel. La ligne du bas est la suivante: vous pouvez empêcher l'insertion de ces lignes, mais vous ne pouvez pas utiliser 'on conflict 'avec cette restriction –