除外制約
代わりに、除外制約を使用することをお勧めします。これは、はるかに簡単、安全、高速です。
追加のモジュールbtree_gist<をインストールする必要があります/ code>
最初。この関連する回答の説明と説明を参照してください:
また、 "ParentID"
を含める必要があります テーブル内"Bar"
冗長に、それは支払うべき小さな価格になります。テーブル定義は次のようになります。
CREATE TABLE "Foo" (
"FooID" serial PRIMARY KEY
"ParentID" int4 NOT NULL REFERENCES "Parent"
"Details1" varchar
CONSTRAINT foo_parent_foo_uni UNIQUE ("ParentID", "FooID") -- required for FK
);
CREATE TABLE "Bar" (
"ParentID" int4 NOT NULL,
"FooID" int4 NOT NULL REFERENCES "Foo" ("FooID"),
"Timerange" tstzrange NOT NULL,
"Detail1" varchar,
"Detail2" varchar,
CONSTRAINT "Bar_pkey" PRIMARY KEY ("FooID", "Timerange"),
CONSTRAINT bar_foo_fk
FOREIGN KEY ("ParentID", "FooID") REFERENCES "Foo" ("ParentID", "FooID"),
CONSTRAINT bar_parent_timerange_excl
EXCLUDE USING gist ("ParentID" WITH =, "Timerange" WITH &&)
);
"Bar"。"FooID"
のデータ型も変更しました int8
から ストライク> int4
へ 。 "Foo"。"FooID"
を参照します 、これは serial
、つまり int4
。一致するタイプint4
を使用します (または単に integer
)いくつかの理由で、そのうちの1つはパフォーマンスです。
トリガーはもう必要ありません(少なくともこのタスクでは必要ありません)。また、インデックス "Bar_FooID_Timerange_idx"
を作成しません。 ストライク> 除外制約によって暗黙的に作成されるため、これ以上はありません。
( "ParentID"、 "FooID")
のbtreeインデックス ただし、おそらく役立つでしょう:
CREATE INDEX bar_parentid_fooid_idx ON "Bar" ("ParentID", "FooID");
関連:
UNIQUE( "ParentID"、 "FooID")
を選択しました "FooID"
が先頭にある別のインデックスがあるため、その逆ではありません。 いずれかの表:
余談ですが、二重引用符で囲まれたCaMeLは使用していません-大文字と小文字の区別 Postgresで。ここでは、レイアウトに準拠するためにのみ行います。
冗長な列を避ける
"Bar"。"ParentID"
を含めることができない、または含めない場合 冗長に、別の不正があります 方法-"Foo"。"ParentID"
という条件で 更新されることはありません 。たとえば、トリガーを使用して、それを確認してください。
IMMUTABLE
を偽造することができます 機能:
CREATE OR REPLACE FUNCTION f_parent_of_foo(int)
RETURNS int AS
'SELECT "ParentID" FROM public."Foo" WHERE "FooID" = $1'
LANGUAGE sql IMMUTABLE;
public
を想定して、確実にテーブル名をスキーマ修飾しました 。スキーマに適応します。
詳細:
次に、それを除外制約で使用します:
CONSTRAINT bar_parent_timerange_excl
EXCLUDE USING gist (f_parent_of_foo("FooID") WITH =, "Timerange" WITH &&)
1つの冗長なint4
を保存している間 列では、制約の検証に費用がかかり、ソリューション全体がより多くの前提条件に依存します。
競合の処理
INSERT
をラップできます およびUPDATE
plpgsql関数に追加し、除外制約から発生する可能性のある例外をトラップします( 23P01 exclude_violation
)何らかの方法で処理します。
INSERT ...
EXCEPTION
WHEN exclusion_violation
THEN -- handle conflict
完全なコード例:
Postgres9.5での競合の処理
Postgresでは9.5 INSERT
を処理できます 新しい「UPSERT」実装で直接。 ドキュメント:
ただし:
ただし、 ON CONFLICT DO NOTHING
は引き続き使用できます。 、したがって、 exclude_violation
の可能性を回避します 例外。実際に更新された行があるかどうかを確認するだけです。これは安価です:
INSERT ...
ON CONFLICT ON CONSTRAINT bar_parent_timerange_excl DO NOTHING;
IF NOT FOUND THEN
-- handle conflict
END IF;
この例では、チェックを指定された除外制約に制限します。 (上記の表の定義で、この目的のために制約に明示的に名前を付けました。)他の考えられる例外はキャッチされません。