42

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

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 列被显式声明为 NULLable,并且作为 PRIMARY KEY 的一部分,它隐含地不可为空。是否有意义?

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

4

4 回答 4

59

因为它PRIMARY KEY NOT NULL 自动生成包含的列。我在这里引用手册

主键约束指定表的一列或多列只能包含唯一(非重复)、非空值。从技术上讲,PRIMARY KEY只是UNIQUENOT NULL的组合。

大胆强调我的。

我进行了测试以确认NOT NULL与约束结合使用完全冗余PRIMARY KEY(在当前实现中,在版本 13 中重新测试)。即使在删除 PK 约束后,约束仍然存在,无论创建时的显式子句NOT NULL如何NOT NULL

CREATE TABLE foo (foo_id int PRIMARY KEY);
ALTER TABLE foo DROP CONSTRAINT foo_pkey;
db=# \d foo
   table »public.foo«
 column |  type   | attribute
--------+---------+-----------
 foo_id | integer | not null    -- stays

db<>在这里摆弄

如果语句NULL中包含相同的行为。CREATE TABLE

NOT NULL如果该列应该是NOT NULL. 如果您稍后决定更改 PK 约束,您可能会忘记标记该列NOT NULL- 或者它是否应该是NOT NULL.

Postgres TODO wiki 中有一个项目可以NOT NULL从 PK 约束中解耦。所以这可能会在未来的版本中改变:

将 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

(目前)100% 相当于:

foo_id INTEGER PRIMARY KEY

在这种情况下,因为NULL被视为干扰词。
我们不希望后者失败。所以这不是一个选择。

于 2013-11-15T16:59:18.093 回答
2

如果没有记错的话,文档会提到:

  • nullin create table 语句基本上是一个被忽略的干扰
  • primary key强制非空和唯一约束

看:

# 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)
于 2013-11-15T16:58:55.417 回答
0

如果正如@ErwinBrandstetter 所说,PRIMARY KEY 只是 UNIQUE 和 NOT NULL 的组合,那么您可以使用不带代替的UNIQUE约束。例子:NOT NULLPRIMARY KEY

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);
于 2014-09-05T21:34:04.880 回答
0

说到 NOT NULL,有很多方法可以确保它。不仅将 PostgreSQL 作为关系数据库引擎说:

  1. 列约束。
  2. 表约束(单个 NOT NULL 或复杂的布尔表达式)
  3. 索引定义,
  4. 将任何 NULL 更改为“其他内容”的触发器。甚至可能还有其他方法。

一个就够了。没有其他人意味着我们有矛盾吗?我不这么认为。

于 2022-02-17T15:09:01.510 回答