В сущности говоря, в NULL в первичном ключе из нескольких столбцов нет ничего плохого. Но иметь такое имеет значение, которое, скорее всего, разработчик не собирался делать, поэтому многие системы выдают ошибку, когда вы пытаетесь это сделать.
Рассмотрим случай версий модулей / пакетов, хранящихся в виде последовательности полей:
CREATE TABLE module
(name varchar(20) PRIMARY KEY,
description text DEFAULT '' NOT NULL);
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20),
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
Первые 5 элементов первичного ключа являются регулярно определяемыми частями версии выпуска, но некоторые пакеты имеют настраиваемое расширение, которое обычно не является целым числом (например, «rc-foo», «vanilla», «beta» или что-то в этом роде). еще кто-то, кому не хватает четырех полей, может придумать). Если пакет не имеет расширения, то в приведенной выше модели он равен NULL, и если оставить все как есть, никакого вреда не будет.
Но что является НУЛЬ? Предполагается, что он представляет собой отсутствие информации, неизвестного. Тем не менее, возможно, это имеет больше смысла:
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20) DEFAULT '' NOT NULL,
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
В этой версии "ext" часть кортежа NOT NULL, но по умолчанию это пустая строка, которая семантически (и практически) отличается от NULL. NULL - это неизвестное, тогда как пустая строка - это преднамеренная запись «чего-то, чего нет». Другими словами, «пустой» и «ноль» - это разные вещи. Разница между «у меня нет значения здесь» и «я не знаю, каково значение здесь».
Когда вы регистрируете пакет, в котором отсутствует расширение версии, вы знаете, , в нем отсутствует расширение, поэтому пустая строка на самом деле является правильным значением. Значение NULL будет правильным только в том случае, если вы не знаете, есть ли у него расширение или нет, или вы знали, что оно имеет, но не знали, что это такое. С такой ситуацией легче справиться в системах, где строковые значения являются нормой, потому что нет способа представить «пустое целое число», кроме вставки 0 или 1, которое будет свернуто в любых последующих сравнениях (которые имеют свои последствия) *.
Кстати, оба способа допустимы в Postgres (поскольку мы обсуждаем RDMBS "на уровне предприятия"), но результаты сравнения могут сильно отличаться, когда вы добавляете NULL в смесь - потому что NULL == "не знаю "так что все результаты сравнения, включающие NULL, заканчиваются тем, что NULL, поскольку вы не можете знать то, что неизвестно. ОПАСНОСТЬ! Тщательно подумайте об этом: это означает, что результаты сравнения NULL распространяются через серию сравнений. Это может быть источником незаметных ошибок при сортировке, сравнении и т. Д.
Postgres предполагает, что вы взрослый человек, и можете принять это решение для себя. Oracle и DB2 предполагают, что вы не понимаете, что делаете что-то глупое, и выдают ошибку. Это обычно правильная вещь, но не всегда - вы могли бы на самом деле не знать и иметь NULL в некоторых случаях и, следовательно, оставить строку с неизвестным элементом, с которым имеют место значимые сравнения невозможно правильное поведение.
В любом случае вы должны стремиться исключить количество полей NULL, разрешенных для всей схемы, и вдвойне, если это касается полей, являющихся частью первичного ключа. В подавляющем большинстве случаев наличие столбцов NULL является признаком ненормализованной (в отличие от преднамеренно ненормализованной) схемы, и о ней следует очень тщательно подумать, прежде чем ее принять.
[* ПРИМЕЧАНИЕ. Можно создать пользовательский тип, представляющий собой объединение целых чисел и «нижнего» типа, который будет семантически означать «пустой», а не «неизвестный». К сожалению, это вносит некоторую сложность в операции сравнения, и, как правило, правильность ввода текста на практике не стоит усилий, так как вам вообще не следует разрешать много значений NULL
. Тем не менее, было бы замечательно, если бы СУБД включали тип BOTTOM
по умолчанию в дополнение к NULL
, чтобы предотвратить привычку случайно связывать семантику «нет значения» с «неизвестным значением». ]