Что случилось с столбцы с нулевым значением в составных первичных ключах?


ORACLE не разрешает значения NULL ни в одном из столбцов, составляющих первичный ключ. Похоже, что то же самое верно и для большинства других систем "корпоративного уровня".

в то же время, большинство систем также позволит уникальный противопоказания для столбцов с нулевым значением.

Почему уникальные ограничения могут иметь нули, но первичные ключи не могут? Есть фундаментальные причины для этого, или это скорее техническое ограничение?

6 121

6 ответов:

первичные ключи предназначены для уникальной идентификации строк. Это делается путем сравнения всех частей ключа к входной.

в определении NULL не может быть частью успешного сравнения. Даже сравнение с самим собой (NULL = NULL) не удастся. Это означает, что ключ, содержащий NULL, не будет работать.

Additonally, NULL разрешено во внешнем ключе, чтобы отметить необязательную связь.(*) позволяя ему в ПК, а также сломать этот.


(*)предупреждение: наличие обнуляемых внешних ключей не является чистым дизайном реляционной базы данных.

если есть два объекта A и B здесь A может быть дополнительно связано с B, чистое решение заключается в создании таблицы разрешения (скажем AB). Эта таблица будет ссылаться A С B: Если есть и отношения, то он будет содержать запись, если там не затем он не будет.

первичный ключ определяет уникальный идентификатор для каждый строку в таблице: если таблица имеет первичный ключ, у вас есть гарантированный способ выбрать любую строку из него.

уникальное ограничение не обязательно идентифицирует каждую строку; оно просто указывает, что если строка имеет значения в своих столбцах,затем они должны быть уникальными. Этого недостаточно для однозначной идентификации каждый строка, что и должен делать первичный ключ.

принципиально ничего не говорю плохого с нулем в нескольких столбцов первичного ключа. Но наличие одного имеет последствия, которые дизайнер, вероятно, не намеревался, поэтому многие системы бросают ошибку, когда вы пытаетесь это сделать.

рассмотрим случай версий модуля / пакета, хранящихся в виде ряда полей:

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 " или что-то еще, для кого четыре полей недостаточно может выдумать). Если пакет не имеет расширения, то он является нулевым в приведенной выше модели, и никакого вреда не будет сделано, оставив вещи таким образом.

а то и a 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 является неизвестным, в то время как пустая строка является преднамеренным определением "чего-то нет". Другими словами," пустой "и" нулевой " - это разные вещи. Это разница между "у меня нет ценности здесь" и "я не знаю, что такое ценность здесь."

при регистрации пакета, в котором отсутствует расширение версии вы знаю it отсутствует расширение, поэтому пустая строка на самом деле является правильным значением. Значение NULL будет правильным только в том случае, если вы не знаете, есть ли у него расширение или нет. С этой ситуацией легче иметь дело в системах, где строковые значения являются нормой, потому что нет никакого способа представить "пустое целое число", кроме вставки 0 или 1, которое будет свернуто в любых сравнениях, сделанных позже (что имеет свои собственные последствия).

кстати, оба способа действительны в Postgres (так как мы обсуждая" enterprise " RDMBSs), но результаты сравнения могут сильно отличаться, когда вы бросаете NULL в микс-потому что NULL == "не знаю", поэтому все результаты сравнения с участием NULL заканчиваются нулем, так как вы не можете знать что-то неизвестное. Таким образом, это может быть источником тонких ошибок при сортировке, сравнении и т. д. Postgres предполагает, что вы взрослый человек и можете принять это решение самостоятельно. Oracle и DB2 предполагают, что вы не поняли, что делаете что-то глупое, и выдаете ошибку. Этот это обычно правильная вещь, но не всегда - вы могли бы на самом деле не знать и иметь NULL в некоторых случаях и, следовательно, оставляя строку с неизвестным элементом, против которого значимые сравнения невозможны, является правильным поведением.

в любом случае вы должны стремиться исключить количество пустых полей, которые вы разрешаете по всей схеме, и вдвойне, когда речь заходит о полях, которые являются частью первичного ключа. В подавляющем большинстве случаев наличие Пустые столбцы-это признак ненормализованного (в отличие от намеренно ненормализованного) дизайна схемы и должны быть очень тщательно продуманы до принятия.

NULL == NULL - > false (по крайней мере, в СУБД)

таким образом, вы не сможете получить какие-либо отношения, используя нулевое значение даже с дополнительными столбцами с реальными значениями.

ответ Тони Эндрюса является достойным. Но реальный ответ заключается в том, что это было соглашение, используемое сообществом реляционных баз данных, и не является необходимостью. Может быть, это хорошая конвенция, а может и нет.

сравнение чего-либо с нулем приводит к неизвестному (3-е значение истины). Так что, как было предложено с нулями, вся традиционная мудрость относительно равенства уходит в окно. Ну вот как это выглядит на первый взгляд.

но я не думаю, что это обязательно так и даже базы данных SQL не думают, что NULL уничтожает все возможности для сравнения.

запустите в своей базе данных запрос ВЫБЕРИТЕ * ИЗ ЗНАЧЕНИЙ (NULL) СОЮЗ ВЫБЕРИТЕ * ИЗ ЗНАЧЕНИЙ (NULL)

вы видите только один кортеж с одним атрибутом, который имеет значение NULL. Таким образом, объединение признало здесь два нулевых значения равными.

при сравнении составного ключа, имеющего 3 компонента, с кортежем с 3 атрибутами (1, 3, NULL) = (1, 3, NULL) 1 = 1 и 3 = 3 и NULL = НОЛЬ Результат этого неизвестен.

но мы могли бы определить новый вид оператора сравнения, например. ==. Х == Г Х = Y ИЛИ (X РАВЕН НУЛЮ, А Y ИМЕЕТ ЗНАЧЕНИЕ NULL)

наличие такого оператора равенства сделало бы составные ключи с нулевыми компонентами или несоставной ключ с нулевым значением беспроблемным.

Я все еще считаю, что это фундаментальный / функциональный недостаток, вызванный технической стороной. Если у вас есть необязательное поле, с помощью которого вы можете идентифицировать клиента, теперь вам нужно взломать фиктивное значение в него, просто потому, что NULL != NULL, не особенно элегантный, но это "отраслевой стандарт"