これは、Postgresで遭遇した1つの奇妙な望ましくない動作です。複合主キーを使用してPostgresテーブルを作成すると、複合組み合わせの各列にNOT NULL制約が適用されます。
例えば、
CREATE TABLE distributors (m_id integer, x_id integer, PRIMARY KEY(m_id, x_id));
列NOT NULL
およびm_id
にx_id
制約を適用しますが、これは不要です! MySQLはこれを行いません。オラクルもそうではないと思います。
PRIMARY KEY
がUNIQUE
およびNOT NULL
を自動的に適用することを理解していますが、これは単一列の主キーの場合に意味があります。複数列の主キーテーブルでは、一意性は組み合わせによって決定されます。
Postgresのこの動作を回避する簡単な方法はありますか?
これを実行すると:
CREATE TABLE distributors (m_id integer, x_id integer);
もちろん、NOT NULL
制約はありません。
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つのオプションが表示されます:
上記の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つ以上の列で手に負えなくなります。
関連:
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データベースがこのルールに従っていない可能性があると聞いています。したがって、移植を目的としたアプリケーションを開発する場合は注意が必要です。
大胆な強調鉱山。