次の表を考えてみます。
_CREATE TABLE verified_name (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
UNIQUE (name, email)
);
CREATE TABLE address (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
verified_name_id INTEGER NULL REFERENCES verified_name(id)
);
_
_address.verified_name_id
_がNULLでない場合、name
のemail
およびaddress
フィールドが参照される_verified_name
_?
以下をaddress
に追加してみました:
_FOREIGN KEY (name, email) REFERENCES verified_name(name, email)
_
...しかし、その制約は_verified_name_id
_がNULLの場合でも適用されます。
_WHERE verified_name_id IS NOT NULL
_のような句を含む partial index 構文に似たものを探していますが、そのような句を_FOREIGN KEY
_制約に追加するだけでは機能しません。
現在の望ましくない解決策:
次の制約を_verified_name
_に追加できます。
_UNIQUE (name, email),
UNIQUE (id, name, email)
_
そして、address
に対する次の制約:
_FOREIGN KEY (verified_name_id, name, email) REFERENCES verified_name(id, name, email)
_
...しかし、これは_verified_name
_に追加したくない制約を作成します(これは有効な論理制約ですが、余計であり、パフォーマンスにわずかな影響があります)。
問題の核心はデータモデルです。 normalizedschema では、name
とemail
を重複して保存することはありません。次のようになります:
CREATE TABLE name (
name_id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
verified BOOLEAN NOT NULL DEFAULT FALSE,
UNIQUE (name, email)
);
CREATE TABLE address (
address_id SERIAL PRIMARY KEY,
name_id INT REFERENCES name(name_id)
...
);
まだ検証されていない名前でUNIQUE制約を破ることを許可する必要がある場合は、それを partial UNIQUE INDEX と置き換えることができます(念頭に置いていたように)。
CREATE UNIQUE INDEX name_verified_idx ON name(name, email) WHERE verified;
不幸な設計に悩まされている一方で、すでに見つけたソリューションは要件に完全に適合しています。 デフォルトのFOREIGN KEY
MATCH SIMPLE
動作は、
WHERE verified_name_id IS NOT NULL
のような句を含む部分インデックス構文。
マニュアルの引用 :
MATCH SIMPLE
では、任意の外部キー列をnullにすることができます。それらのいずれかがnullの場合、その行は参照されるテーブルで一致する必要はありません。
(やや信頼性が低く、費用がかかる)代替案は、address
のINSERT/UPDATEのトリガーとverified_name
のINSERT/UPDATE/DELETEのトリガーです。