В дополнение к тому, что предоставил @Craig (и исправляет некоторые из них):
Эффективное Postgres 9.4 , UNIQUE
, PRIMARY KEY
и EXCLUDE
ограничения проверяются сразу после каждой строки , когда определено NOT DEFERRABLE
. Это отличается от других видов NOT DEFERRABLE
ограничений (только в настоящее время REFERENCES
(внешний ключ)), которые проверяются после каждого оператора . Мы работали над всем этим под вопросом, связанным с SO:
Это не достаточно для UNIQUE
(или PRIMARY KEY
или EXCLUDE
ограничение) , чтобы быть , DEFERRABLE
чтобы сделать ваш код , представленный с несколькими отчетности работы.
И вы не можете использовать ALTER TABLE ... ALTER CONSTRAINT
для этой цели. По документации:
ALTER CONSTRAINT
Эта форма изменяет атрибуты ограничения, которое было создано ранее. В настоящее время могут быть изменены только ограничения внешнего ключа .
Жирный акцент мой. Используйте вместо этого:
ALTER TABLE t
DROP CONSTRAINT category_name_key
, ADD CONSTRAINT category_name_key UNIQUE(name) DEFERRABLE;
Отбросьте и добавьте ограничение обратно в одном выражении, чтобы у всех не было временного промежутка, чтобы проникнуть в поврежденные строки. Для больших таблиц было бы заманчиво каким-то образом сохранить базовый уникальный индекс, поскольку его удаление и повторное создание обходится дорого. Увы, это не представляется возможным при использовании стандартных инструментов (если у вас есть решение для этого, пожалуйста, сообщите нам об этом!):
Для одного оператора достаточно сделать ограничение отложенным:
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;
Запрос с CTE также является одним оператором:
WITH x AS (
UPDATE category SET name = 'phones' WHERE id = 1
)
UPDATE category SET name = 'tablets' WHERE id = 2;
Однако для вашего кода с несколькими операторами вам (дополнительно) необходимо фактически отложить ограничение - или определить его как INITIALLY DEFERRED
Either, как правило, дороже, чем выше. Но может быть нелегко собрать все в одно утверждение.
BEGIN;
SET CONSTRAINTS category_name_key DEFERRED;
UPDATE category SET name = 'phones' WHERE id = 1;
UPDATE category SET name = 'tablets' WHERE id = 2;
COMMIT;
Имейте в виду ограничение в связи с FOREIGN KEY
ограничениями, однако. По документации:
Столбцы, на которые ссылаются, должны быть столбцами неотложного ограничения уникального или первичного ключа в таблице, на которую ссылаются.
Таким образом, вы не можете иметь и то и другое одновременно.