En plus de ce que @Craig a fourni (et en corrige une partie):
À compter Postgres 9.4 , UNIQUE
, PRIMARY KEY
et les EXCLUDE
contraintes sont vérifiées immédiatement après chaque ligne lorsqu'elle est définie NOT DEFERRABLE
. Ceci est différent des autres types de NOT DEFERRABLE
contraintes (actuellement uniquement REFERENCES
(clé étrangère)) qui sont vérifiées après chaque instruction . Nous avons travaillé tout cela sous cette question connexe sur SO:
Il ne suffit pas qu'une contrainte UNIQUE
(ou PRIMARY KEY
ou EXCLUDE
) soit DEFERRABLE
pour faire fonctionner votre code présenté avec plusieurs instructions .
Et vous ne pouvez pas l' utiliser ALTER TABLE ... ALTER CONSTRAINT
à cette fin. Par documentation:
ALTER CONSTRAINT
Ce formulaire modifie les attributs d'une contrainte précédemment créée. Actuellement, seules les contraintes de clé étrangère peuvent être modifiées .
Accentuation sur moi. Utilisez plutôt:
ALTER TABLE t
DROP CONSTRAINT category_name_key
, ADD CONSTRAINT category_name_key UNIQUE(name) DEFERRABLE;
Supprimez et ajoutez la contrainte dans une seule instruction afin qu'il n'y ait pas de fenêtre temporelle pour que quiconque se glisse dans les lignes incriminées. Pour les grandes tables, il serait tentant de conserver l'index unique sous-jacent d'une manière ou d'une autre, car il est coûteux de le supprimer et de le recréer. Hélas, cela ne semble pas possible avec des outils standards (si vous avez une solution pour cela, faites-le nous savoir!):
Pour une seule déclaration rendant la contrainte reportable suffit:
UPDATE category c
SET name = c_old.name
FROM category c_old
WHERE c.id IN (1,2)
AND c_old.id IN (1,2)
AND c.id <> c_old.id;
Une requête avec CTE est également une seule instruction:
WITH x AS (
UPDATE category SET name = 'phones' WHERE id = 1
)
UPDATE category SET name = 'tablets' WHERE id = 2;
Cependant , pour votre code avec plusieurs instructions, vous devez (en plus) différer réellement la contrainte - ou la définir comme INITIALLY DEFERRED
Soit est généralement plus cher que ce qui précède. Mais il peut ne pas être facile de tout regrouper en une seule instruction.
BEGIN;
SET CONSTRAINTS category_name_key DEFERRED;
UPDATE category SET name = 'phones' WHERE id = 1;
UPDATE category SET name = 'tablets' WHERE id = 2;
COMMIT;
Soyez conscient d'une limitation liée aux FOREIGN KEY
contraintes. Par documentation:
Les colonnes référencées doivent être les colonnes d'une contrainte de clé unique ou primaire non reportable dans la table référencée.
Vous ne pouvez donc pas avoir les deux en même temps.