web-dev-qa-db-ja.com

null許容列にPRIMARY KEYを含むテーブルを作成できるのはなぜですか?

次のコードは、エラーを発生させずにテーブルを作成します。

CREATE TABLE test(
ID INTEGER NULL,
CONSTRAINT PK_test PRIMARY KEY(ID)
)

期待どおりにNULLを挿入できないことに注意してください。

INSERT INTO test
VALUES(1),(NULL)
ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null).
********** Error **********

ERROR: null value in column "id" violates not-null constraint
SQL state: 23502
Detail: Failing row contains (null).

なぜ自己矛盾した定義でテーブルを作成できるのですか? ID列は、PRIMARY KEYの一部として、NULL可能として明示的に宣言されており、暗黙的にNULL可能ではありません。それは意味がありますか?

編集:この自己矛盾するCREATE TABLEがちょうどそこで失敗した場合、それは良くないでしょうか?

27
A-K

PRIMARY KEYは列を作成するためNOT NULLは自動的に。引用します マニュアルはこちら

主キー制約は、テーブルの1つまたは複数の列に、一意の(重複していない)非null値のみを含めることができることを指定します。技術的には、PRIMARY KEYUNIQUENOT NULLの組み合わせにすぎません。

大胆な強調鉱山。

(私の以前の信念に対して)NOT NULLPRIMARY KEY制約と組み合わせて完全に冗長であることを確認するためにテストを実行しました(現在の実装では、バージョン9.5まで)。 NOT NULL制約は、作成時に明示的なNOT NULL句に関係なく、PK制約を削除した後も残ります

db=# CREATE TEMP TABLE foo (foo_id int PRIMARY KEY);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo"
CREATE TABLE

db=# ALTER TABLE foo DROP CONSTRAINT foo_pkey;
ALTER TABLE

db=# \d foo
   table »pg_temp_4.foo«
 column |  type   | attribute
--------+---------+-----------
 foo_id | integer | not null

NULLCREATEステートメントに含まれている場合の動作は同じです。

ただし、列がNOT NULLであると想定されている場合でも、コードリポジトリにNOT NULLを重複して保持しても問題はありません。後でpk制約を移動することを決定した場合、列にNOT NULLをマークするのを忘れるか、またはNOT NULLであると想定されていたかどうかを忘れることがあります。

PK制約からNOT NULLを分離するための Postgres TODO wikiの項目 があります。したがって、これは将来のバージョンで変更される可能性があります。

NOT NULL制約情報をpg_constraintに移動

現在、NOT NULL制約は、その起源を指定せずにpg_attributeに保存されています。主キー。 1つの明白な問題は、PRIMARY KEY制約を削除してもNOT NULL制約の指定が削除されないことです。もう1つの問題は、CHECK制約と同じように、NOT NULLを親テーブルから子に強制的に伝搬する必要があることです。 (しかし、PRIMARY KEYを削除すると子供に影響がありますか?)

追加された質問への回答:

この自己矛盾するCREATE TABLEがすぐに失敗した方がいいのではないでしょうか。

上記で説明したように、これは

foo_id INTEGER NULL PRIMARY KEY

以下と同等です。

foo_id INTEGER PRIMARY KEY

NULLはノイズワードとして扱われるため。
そして、後者が失敗することを望まないでしょう。したがって、これはオプションではありません。

33

メモリが役立つ場合、ドキュメントは次のように述べています:

  • create tableステートメントのnullは基本的に無視されるノイズワードです
  • primary keyは、nullではなく、一意の制約を強制します

見る:

# create table test (id int null primary key);
CREATE TABLE
# \d test
     Table "public.test"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | not null
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)
2

@ErwinBrandstetterが言ったように、PRIMARY KEYはUNIQUEとNOT NULLの単なる組み合わせの場合、NOT NULLの代わりにPRIMARY KEYなしでUNIQUE制約を使用できます。例:

CREATE TABLE test(
    id integer,
    CONSTRAINT test_id_key UNIQUE(id)
);

このようにして、次のようなことができます。

INSERT INTO test (id) VALUES (NULL);
INSERT INTO test (id) VALUES (NULL);
INSERT INTO test (id) VALUES (NULL);
1