На додаток до того, що надав @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
або, як правило, дорожче, ніж вище. Але це може бути непросто зв'язати все в одне твердження.
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
обмеженнями. За документацію:
Колонки, на які посилаються, повинні бути стовпцями обмеження унікального або первинного ключа, що не відкладається, у таблиці з посиланням.
Отже, ви не можете мати обох одночасно.