@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;
制約をドロップして1つのステートメントに追加し直して、問題のある行に侵入する時間枠がないようにします。大きなテーブルの場合、削除と再作成にはコストがかかるため、基になる一意のインデックスを何らかの方法で保存するのは魅力的です。残念ながら、それは標準ツールでは不可能なようです(そのためのソリューションがある場合はお知らせください!):
制約を遅延可能にする単一のステートメントの場合:
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
、通常どちらかが上記よりも高価になります。しかし、すべてを1つのステートメントにまとめるのは容易ではありません。
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
。ドキュメントごと:
参照される列は、 は、参照されるテーブルの非遅延一意または主キー制約の。
したがって、両方を同時に使用することはできません。