postgresの遅延可能な一意のインデックス


14

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;使用されています)。

私の仮定は正しいですか、もしそうなら、意図した動作を達成する方法はありますか?

ありがとう

回答:


15

インデックスは延期できません- UNIQUE部分的であるかどうかに関係なく、UNIQUE制約のみです。制約の他のタイプ(FOREIGN KEYPRIMARY KEYEXCLUDEではない- )も遅延可能です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場合、0でない場合は1です)。


別の方法は、(ab)use EXCLUDE制約です:

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で

上記のこと:

  • CASE式はなったNULLとき、booking_status私たちは書くことができる1よりnullまたは異なってい(CASE WHEN booking_status = 1 THEN TRUE END)(booking_status = 1 OR NULL)これ以上明確にしている場合。

  • 一意制約および除外制約は、1つ以上の式がNULLである行を受け入れます。そのため、フィルター付きインデックスとして機能しますWHERE booking_status = 1ます。

  • すべてのWITH演算子は=そうなので、UNIQUE制約ます。

  • これら2つの組み合わせにより、制約はフィルター処理された一意のインデックスとして機能します。

  • しかし、それは制約であり、EXCLUDE制約は延期できます。


2
EXCLUDEバージョンの+1は、必要なものでした。EXCLUDEの機能を示す別の例を次に示します。cybertec
ベンジャミンピーター

(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)) WHERE (booking_status = 1)「除外制約はインデックスを使用して実装される」ため、に置き換える必要があり、この部分インデックスWHEREはより小さく高速になります-postgresql.org/docs/current/sql-createtable.htmlおよびpostgresql.org/docs/current/sql- createindex.html
デニスRyzhkov

1

この質問は何年も経ちましたが、スペイン語話者のために、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では、各行に対して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ミリ秒、合計時間:0ミリ秒)クエリOK、影響を受ける1328行(実行時間:858ミリ秒、合計時間:858ミリ秒)エラー:llave重複した複製:Ya presente la llave(div_nkit)=(1338)。

ここでは、最初の完全な文全体(1328行)を実行するため、SIでは主キーを変更できます。しかし、トランザクション(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 **の各文を各文ごとに個別に実行すると、検証は行われませんが、矛盾が見つかった場所で最終的なCOMMITが実行されるため、INSERTに対してエラーは生成されません。


英語の完全な情報については、リンクを確認することをお勧めします。

遅延可能なSQL制約の詳細

DEFERRABLEではなくDEFERRABLEの初期状態

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.