Un index ne peut pas être différé - peu importe qu'il soit UNIQUE
ou non, partiel ou non, seulement une UNIQUE
contrainte. D' autres types de contraintes ( FOREIGN KEY
, PRIMARY KEY
, EXCLUDE
) sont également reportables - mais pas les CHECK
contraintes.
Ainsi, l'index partiel unique (et la contrainte implicite qu'il implémente) sera vérifié à chaque instruction (et en fait après chaque insertion / mise à jour de ligne dans l'implémentation actuelle), pas à la fin de la transaction.
Ce que vous pourriez faire, si vous souhaitez implémenter cette contrainte comme reportable, est d'ajouter une table de plus dans la conception. Quelque chose comme ça:
CREATE TABLE public.booking_status
( booking_id int NOT NULL, -- same types
check_in timestamp NOT NULL, -- as in
check_out timestamp NOT NULL, -- booking
CONSTRAINT unique_booking
UNIQUE (check_in, check_out)
DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT unique_booking_fk
FOREIGN KEY (booking_id, check_in, check_out)
REFERENCES public.booking (booking_id, check_in, check_out)
DEFERRABLE INITIALLY DEFERRED
) ;
Avec cette conception et en supposant qu'il booking_status
n'a que 2 options possibles (0 et 1), vous pouvez le supprimer entièrement de booking
(s'il y a une ligne à booking_status
, c'est 1, sinon 0).
Une autre façon serait (ab) d'utiliser une EXCLUDE
contrainte:
ALTER TABLE booking
ADD CONSTRAINT unique_booking
EXCLUDE
( check_in WITH =,
check_out WITH =,
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =
)
DEFERRABLE INITIALLY DEFERRED ;
Testé chez dbfiddle .
Ce que fait ce qui précède:
L' CASE
expression devient NULL
quand booking_status
est nulle ou différente de 1. On pourrait écrire (CASE WHEN booking_status = 1 THEN TRUE END)
comme (booking_status = 1 OR NULL)
si cela le rendait plus clair.
Les contraintes uniques et d'exclusion acceptent les lignes où une ou plusieurs des expressions sont NULL. Il agit donc comme un index filtré avec WHERE booking_status = 1
.
Tous les WITH
opérateurs le sont =
donc il agit comme une UNIQUE
contrainte.
Ces deux combinés font agir la contrainte comme un index unique filtré.
Mais c'est une contrainte et les EXCLUDE
contraintes peuvent être différées.
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)
devrait être remplacé par) WHERE (booking_status = 1)
car "Les contraintes d'exclusion sont implémentées à l'aide d'un index", et cet index partiel avecWHERE
sera plus petit et plus rapide - postgresql.org/docs/current/sql-createtable.html et postgresql.org/docs/current/sql- createindex.htmlBien que les années de cette question soient passées, je voudrais clarifier pour les hispanophones, les tests ont été effectués dans Postgres:
La contrainte suivante a été ajoutée à un tableau de 1337 enregistrements, où le kit est la clé primaire:
Cela crée une clé primaire par défaut NON DEFERRED pour la table, donc lors de la prochaine mise à jour, nous obtenons une erreur:
Dans Postgres, l'exécution d'une MISE À JOUR pour chaque ROW vérifie que la RESTRICTION ou la CONTRAINTE est respectée.
Le CONSTRAINT IMMEDIATE est maintenant créé et chaque instruction est exécutée séparément:
Ici, SI permet de changer la clé primaire car il exécute toute la première phrase complète (1328 lignes); mais bien qu'il soit en transaction (BEGIN), la CONTRAINTE est validée immédiatement à la fin de chaque phrase sans avoir effectué COMMIT, génère donc l'erreur lors de l'exécution de INSERT. Enfin, nous avons créé le CONSTRAINT DEFERRED, procédez comme suit:
Si nous exécutons chaque instruction du ** bloc 2 **, chaque phrase séparément, aucune erreur n'est générée dans l'INSERT car il ne valide pas mais le COMMIT final est exécuté là où il trouve une incohérence.
Pour des informations complètes en anglais, je vous suggère de vérifier les liens:
Contraintes SQL reportables en profondeur
NON DÉFÉRRABLE versus DÉFÉRRABLE INITIALEMENT IMMÉDIATE
la source