为什么我可以在可空列上创建一个带有PRIMARY KEY的表?

A-K*_*A-K 24 postgresql ddl constraints primary-key postgresql-9.3

以下代码创建表而不引发任何错误:

CREATE TABLE test(
ID INTEGER NULL,
CONSTRAINT PK_test PRIMARY KEY(ID)
)
Run Code Online (Sandbox Code Playgroud)

请注意,我无法按预期插入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).
Run Code Online (Sandbox Code Playgroud)

为什么我可以创建一个具有自相矛盾定义的表格?ID列显式声明为NULLable,并且作为PRIMARY KEY的一部分,它隐式地不可为空.是否有意义?

编辑:如果这个自相矛盾的CREATE TABLE在那里失败了会不会更好?

Erw*_*ter 33

由于PRIMARY KEY 使NOT NULL 自动.我在这里引用手册:

主键约束指定表的一列或多列只能包含唯一(非重复)非空值.从技术上讲,PRIMARY KEY仅仅是UNIQUE和的结合NOT NULL.

大胆强调我的.

我运行了一个测试,以确认(与我以前的信念相反!)NOT NULLPRIMARY KEY约束相结合是完全冗余的(在当前实现中,直到版本9.5).删除PK约束后,NOT NULL约束仍然存在,无论NOT NULL创建时的显式子句如何.

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
Run Code Online (Sandbox Code Playgroud)

db=# \d foo
   table »pg_temp_4.foo«
 column |  type   | attribute
--------+---------+-----------
 foo_id | integer | not null
Run Code Online (Sandbox Code Playgroud)

如果声明中NULL包含相同的行为CREATE.

但是,NOT NULL如果列应该是在代码存储库中保持冗余仍然不会有害NOT NULL.如果你以后决定移动pk约束,你可能会忘记标记列NOT NULL- 或者它是否应该是NOT NULL.

Postgres TODO wiki中有一个项目可以与NOT NULLPK约束分离.所以这可能会在未来的版本中改变:

将NOT NULL约束信息移动到pg_constraint

目前,NOT NULL约束存储在pg_attribute中,而不指定其起源,例如主键.一个明显的问题是删除PRIMARY KEY约束不会删除NOT NULL约束指定.另一个问题是我们应该强制NOT NULL从父表传播到子表,就像CHECK约束一样.(但是,丢弃PRIMARY KEY会影响孩子吗?)

回答补充问题:

如果这个自相矛盾的CREATE TABLE就在那里失败了,那会不会更好?

如上所述,这

foo_id INTEGER NULL PRIMARY KEY
Run Code Online (Sandbox Code Playgroud)

相当于:

foo_id INTEGER PRIMARY KEY
Run Code Online (Sandbox Code Playgroud)

因为NULL被视为噪音词.
我们不希望后者失败.所以这不是一个选择.