Почему я могу создать таблицу с 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).

Почему я могу создать таблицу с самопротиворечивым определением? Идентификатор явно объявлен как NULLable, и он неявно не является нулевым, как часть PRIMARY KEY. Имеет ли смысл?

Изменить: не было бы лучше, если бы этот самопротиворечивый CREATE TABLE просто не удался прямо там?

Ответы

Ответ 1

Поскольку PRIMARY KEY делает столбец NOT NULL автоматически. Я цитирую руководство здесь:

Ограничение первичного ключа указывает, что столбец или столбцы таблица может содержать только уникальные (не дублирующиеся), ненулевые значения. Технически PRIMARY KEY представляет собой просто комбинацию UNIQUE и NOT NULL.

Смелый акцент мой.

Я проверил тест, чтобы подтвердить это (против моего прежнего убеждения!) NOT NULL полностью избыточно в сочетании с ограничением PRIMARY KEY (в текущей реализации, вплоть до версии 9.5). Ограничение NOT NULL остается после того, как вы сбросите ограничение PK, независимо от явного предложения 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

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

Идентичное поведение, если NULL включено в оператор CREATE.

Однако для сохранения NOT NULL избыточности в репозиториях кода, по-видимому, должно быть NOT NULL. Если позже вы решите переместить ограничение pk, вы можете забыть отметить столбец NOT NULL - или он должен был даже быть NOT NULL.

В Wiki Postgres TODO есть элемент , чтобы отделить NOT NULL от ограничения PK. Таким образом, это может измениться в будущих версиях:

Переместить NOT NULL информацию ограничения на pg_constraint

В настоящее время ограничения NOT NULL хранятся в pg_attribute без указания их происхождения, например. первичные ключи. Один манифест проблема в том, что сброс ограничения PRIMARY KEY не устраняет NOT NULL. Другая проблема заключается в том, что мы должны вероятно, вынуждает NOT NULL распространяться из родительских таблиц в дети, как и ограничения CHECK. (Но затем падает ПЕРВИЧНЫЙ КЛЮЧ влияет на детей?)

Ответьте на добавленный вопрос:

Разве не лучше, если бы этот противоречивый CREATE TABLE просто не удалось?

Как объяснялось выше, этот

foo_id INTEGER NULL PRIMARY KEY

эквивалентно:

foo_id INTEGER PRIMARY KEY

Так как NULL рассматривается как шумовое слово.
И мы не хотим, чтобы последний потерпел неудачу. Так что это не вариант.

Ответ 2

Если память используется, в документах указывается, что:

  • null в операторах create table в основном представляет собой шумовое слово, которое игнорируется
  • 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)

Ответ 3

Если, как сказал @ErwinBrandstetter, PRIMARY KEY - это просто комбинация UNIQUE и NOT NULL, вы можете использовать ограничение UNIQUE без NOT NULL вместо PRIMARY 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);