web-dev-qa-db-ja.com

複合PRIMARY KEYは関連する列にNOT NULL制約を適用します

これは、Postgresで遭遇した1つの奇妙な望ましくない動作です。複合主キーを使用してPostgresテーブルを作成すると、複合組み合わせの各列にNOT NULL制約が適用されます。

例えば、

CREATE TABLE distributors (m_id integer, x_id integer, PRIMARY KEY(m_id, x_id));

NOT NULLおよびm_idx_id制約を適用しますが、これは不要です! MySQLはこれを行いません。オラクルもそうではないと思います。

PRIMARY KEYUNIQUEおよびNOT NULLを自動的に適用することを理解していますが、これは単一列の主キーの場合に意味があります。複数列の主キーテーブルでは、一意性は組み合わせによって決定されます。

Postgresのこの動作を回避する簡単な方法はありますか?
これを実行すると:

CREATE TABLE distributors (m_id integer, x_id integer);

もちろん、NOT NULL制約はありません。

22
user3422637

NULL値を許可する必要がある必要がある場合UNIQUE制約PRIMARY KEYの代わりに(そして代理PK列を追加して、 serial をお勧めします)。これにより、列をNULLにすることができます。

CREATE TABLE distributor (
   distributor_id serial PRIMARY KEY
 , m_id integer
 , x_id integer
 , UNIQUE(m_id, x_id)
);

ただし、( ドキュメントごと ):

一意の制約上、null値は等しいとは見なされません。

あなたの場合、制約に違反することなく、(1, NULL)(m_id, x_id)を何度でも入力できます。 Postgresは、SQL標準の定義に従って、2つのNULL値equalを考慮しません。

NULLの値を等しいものとして扱い、そのような「重複」を禁止する必要がある場合、2つのオプションが表示されます

1. 2つの部分インデックス

上記のUNIQUE制約に加えて

CREATE UNIQUE INDEX dist_m_uni_idx ON distributor (m_id) WHERE x_id IS NULL;
CREATE UNIQUE INDEX dist_x_uni_idx ON distributor (x_id) WHERE m_id IS NULL;

しかし、これはNULLになる可能性のある3つ以上の列で手に負えなくなります。
関連:

2.式の複数列UNIQUEインデックス

UNIQUE制約の代わりに。 -1のように、関連する列に決して存在しない無料のデフォルト値が必要です。禁止するためにCHECK制約を追加します。

CREATE TABLE distributor (
   distributor serial PRIMARY KEY
 , m_id integer
 , x_id integer
 , CHECK (m_id <> -1)
 , CHECK (x_id <> -1)
);
CREATE UNIQUE INDEX distributor_uni_idx ON distributor (COALESCE(m_id, -1)
                                                      , COALESCE(x_id, -1))

特定のRDBMSがどのように処理するかは、適切な動作のインジケータとして常に役立つとは限りません。 Postgresマニュアルはこれを示唆しています

つまり、一意の制約が存在する場合でも、制約された列の少なくとも1つにnull値を含む重複行を格納することが可能です。この動作はSQL標準に準拠していますが、他のSQLデータベースがこのルールに従っていない可能性があると聞いています。したがって、移植を目的としたアプリケーションを開発する場合は注意が必要です。

大胆な強調鉱山。

40