По сути, нет ничего плохого в 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, и если оставить все как есть, никакого вреда не будет.
Но что такое 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» часть кортежа НЕ NULL, но по умолчанию это пустая строка, которая семантически (и практически) отличается от NULL. NULL - это неизвестное, тогда как пустая строка - это преднамеренная запись «чего-то, чего нет». Другими словами, «пустой» и «ноль» - разные вещи. Разница между «у меня нет значения здесь» и «я не знаю, каково значение здесь».
Когда вы регистрируете пакет, в котором отсутствует расширение версии, вы знаете, что в нем нет расширения, поэтому пустая строка на самом деле является правильным значением. Значение NULL будет правильным только в том случае, если вы не знаете, есть ли у него расширение или нет, или вы знали, что оно имеет, но не знали, что это такое. С такой ситуацией легче справиться в системах, где строковые значения являются нормой, потому что нет способа представить «пустое целое число», кроме вставки 0 или 1, которое будет свернуто при любых сравнениях, сделанных позже (которые имеют свои последствия) *.
Кстати, оба способа действительны в Postgres (поскольку мы обсуждаем «корпоративные» RDMBS), но результаты сравнения могут немного отличаться, когда вы добавляете NULL в смесь - потому что NULL == «не знаю», так что все результаты сравнения, включающего NULL, заканчиваются тем, что NULL, поскольку вы не можете знать то, что неизвестно. ОПАСНОСТЬ! Тщательно подумайте об этом: это означает, что результаты сравнения NULL распространяются через серию сравнений. Это может быть источником незаметных ошибок при сортировке, сравнении и т. Д.
Postgres предполагает, что вы взрослый человек, и можете принять это решение самостоятельно. Oracle и DB2 предполагают, что вы не поняли, что делаете что-то глупое, и выдавали ошибку. Это , как правило , правильно, но не всегда - вы , возможно , на самом деле не знаю , и имеют значение NULL в некоторых случаях и , следовательно , оставляя строку с неизвестным элементом , против которого содержательные сравнения невозможны это правильное поведение.
В любом случае вы должны стремиться исключить количество полей NULL, разрешенных для всей схемы, и вдвойне, если это касается полей, являющихся частью первичного ключа. В подавляющем большинстве случаев наличие столбцов NULL является признаком ненормализованной (в отличие от преднамеренно ненормализованной) схемы, и о ней следует очень тщательно подумать, прежде чем ее принять.
[* ПРИМЕЧАНИЕ. Можно создать пользовательский тип, представляющий собой объединение целых чисел и «нижнего» типа, который будет семантически означать «пустой», а не «неизвестный». К сожалению, это вносит некоторую сложность в операции сравнения, и, как правило, правильность ввода текста на практике не стоит усилий, так как вам вообще не следует разрешать много NULL
значений. Тем не менее, было бы замечательно, если бы СУБД включали BOTTOM
тип по умолчанию в дополнение к NULL
предотвращению привычки случайно связывать семантику «нет значения» с «неизвестным значением». ]