alter tableのpostgresドキュメント を見ると、通常の制約はDEFERRABLE
(より具体的にはINITIALLY DEFERRED
、これは私が興味を持っていることです)。
インデックスは、次の条件を満たす限り、制約に関連付けることもできます。
インデックスに式列を含めることも、部分インデックスにすることもできません
これにより、現在、次のような条件を持つ一意のインデックスを作成する方法はないと考えられます。
CREATE UNIQUE INDEX unique_booking
ON public.booking
USING btree
(check_in, check_out)
WHERE booking_status = 1;
することが INITIALLY DEFERRED
、つまり、一意性「制約」はトランザクションの終了時にのみ検証されることを意味します(SET CONSTRAINTS ALL DEFERRED;
使用されている)。
私の仮定は正しいですか。正しい場合、意図した動作を実現する方法はありますか?
ありがとう
インデックスを据え置くことはできません-UNIQUE
であるかどうか、部分的であるかどうかに関係なく、UNIQUE
制約のみです。他のタイプの制約(FOREIGN KEY
、PRIMARY KEY
、EXCLUDE
)も延期可能ですが、CHECK
制約は遅延できません。
したがって、一意の部分インデックス(およびそれが実装する暗黙の制約)は、トランザクションの最後ではなく、すべてのステートメントで(そして実際には現在の実装でのすべての行の挿入/更新後に)チェックされます。
この制約を据え置きとして実装したい場合にできることは、デザインにテーブルをもう1つ追加することです。このようなもの:
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
) ;
この設計では、booking_status
には2つのオプション(0と1)しかないと仮定すると、booking
から完全に削除できます(booking_status
に行がある場合、1です)ない場合は0)。
もう1つの方法は、EXCLUDE
制約を(ab)使用することです。
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 ;
dbfiddleでテストされています。
上記の内容:
booking_status
がnullまたは1以外の場合、CASE
式はNULL
になります。(CASE WHEN booking_status = 1 THEN TRUE END)
を(booking_status = 1 OR NULL)
と書くと、さらに明確になります。
一意制約と除外制約は、1つ以上の式がNULLである行を受け入れます。したがって、これはWHERE booking_status = 1
でフィルターされたインデックスとして機能します。
WITH
演算子はすべて=
であるため、UNIQUE
制約として機能します。
これら2つを組み合わせると、制約はフィルター処理された一意のインデックスとして機能します。
しかし、これは制約であり、EXCLUDE
制約は据え置くことができます。
この質問は何年も経過していますが、スペイン語を話す人のために明確にしたいと思います。テストはPostgresで行われました。
次の制約が1337レコードのテーブルに追加されました。ここで、キットは主キーです。
**Bloque 1**
ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
これにより、テーブルのデフォルトの主キーNOT DEFERREDが作成されるため、次のUPDATEを試行するとエラーが発生します。
update ele_kitscompletos
set div_nkit = div_nkit + 1;
エラー:重複するキーは一意性制限"unique_div_nkit"に違反しています
Postgresでは、各ROWに対してUPDATEを実行することで、RESTRICTIONまたはCONSTRAINTが満たされていることを確認します。
CONSTRAINT IMMEDIATEが作成され、各ステートメントが個別に実行されます。
ALTER TABLE ele_kitscompletos
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY IMMEDIATE
**Bloque 2**
BEGIN;
UPDATE ele_kitscompletos set div_nkit = div_nkit + 1;
INSERT INTO public.ele_kitscompletos(div_nkit, otro_campo)
VALUES
(1338, '888150502');
COMMIT;
クエリはOK、0行が影響を受けました(実行時間:0 ms;合計時間:0 ms)クエリはOK、1328行が影響を受けました(実行時間:858 ms;合計時間:858 ms)エラー:llave duplicada violarestricciónde unicidad"unique_div_nkit"DETAIL :存在します(div_nkit)=(1338)。
ここでSIは、最初の完全な文全体(1328行)を実行するため、主キーを変更できます。ただし、トランザクション(BEGIN)内ではありますが、CONSTRAINTはCOMMITを行わずに各文が終了するとすぐに検証されるため、INSERTを実行するとエラーが発生します。最後にCONSTRAINT DEFERREDを作成して、次のことを行います。
**Bloque 3**
ALTER TABLE public.ele_edivipol
DROP CONSTRAINT unique_div_nkit RESTRICT;
ALTER TABLE ele_edivipol
ADD CONSTRAINT unique_div_nkit
PRIMARY KEY (div_nkit)
DEFERRABLE INITIALLY DEFERRED
**ブロック2 **の各ステートメントを、各文を個別に実行すると、検証されないためINSERTにエラーは生成されませんが、矛盾が見つかった場所で最後のCOMMITが実行されます。
英語での完全な情報については、リンクを確認することをお勧めします。