ORACLE не разрешает значения NULL ни в одном из столбцов, которые содержат первичный ключ. Похоже, то же самое верно для большинства других систем «уровня предприятия».
В то же время большинство систем также допускают уникальные ограничения для столбцов, допускающих обнуляемость.
Почему уникальные ограничения могут иметь значения NULL, а первичные ключи - нет? Есть ли фундаментальная логическая причина для этого или это скорее техническое ограничение?
database
database-design
Роман Старков
источник
источник
Ответы:
Первичные ключи предназначены для уникальной идентификации строк. Это делается путем сравнения всех частей ключа с вводом.
По определению, NULL не может быть частью успешного сравнения. Даже сравнение с собой (
NULL = NULL
) не удастся. Это означает, что ключ, содержащий NULL, не будет работать.Кроме того, NULL допускается во внешнем ключе для обозначения необязательного отношения. (*) Разрешение этого в PK также сломало бы это.
(*) Предупреждение: наличие обнуляемых внешних ключей не является чистым дизайном реляционной базы данных.
Если есть две сущности,
A
иB
где ониA
могут быть необязательно связаныB
, чистое решение состоит в том, чтобы создать таблицу разрешения (скажемAB
). Эта таблица будет связатьA
сB
: Если есть отношения , то она будет содержать запись, если не то не будет.источник
Первичный ключ определяет уникальный идентификатор для каждой строки в таблице: если у таблицы есть первичный ключ, у вас есть гарантированный способ выбрать любую строку из нее.
Уникальное ограничение не обязательно идентифицирует каждую строку; он просто указывает , что если строка имеет значения в своих столбцах, то они должны быть уникальными. Этого недостаточно для однозначной идентификации каждой строки, что и должен делать первичный ключ.
источник
По сути, нет ничего плохого в NULL в первичном ключе из нескольких столбцов. Но иметь такое имеет значение, которое, вероятно, разработчик не собирался делать, поэтому многие системы выдают ошибку, когда вы пытаетесь это сделать.
Рассмотрим случай версий модуля / пакета, хранящихся в виде последовательности полей:
Первые 5 элементов первичного ключа являются регулярно определяемыми частями версии выпуска, но некоторые пакеты имеют настраиваемое расширение, которое обычно не является целым числом (например, «rc-foo», «vanilla» или «beta» или кем-то еще для кому четыре поля недостаточно, может придумать). Если пакет не имеет расширения, то в приведенной выше модели он равен NULL, и если оставить все как есть, никакого вреда не будет.
Но что такое NULL? Предполагается, что это нехватка информации, неизвестность. Тем не менее, возможно, это имеет больше смысла:
В этой версии «ext» часть кортежа НЕ NULL, но по умолчанию это пустая строка, которая семантически (и практически) отличается от NULL. NULL - это неизвестное, тогда как пустая строка - это преднамеренная запись «чего-то, чего нет». Другими словами, «пустой» и «ноль» - разные вещи. Разница между «у меня нет значения здесь» и «я не знаю, каково значение здесь».
Когда вы регистрируете пакет, в котором отсутствует расширение версии, вы знаете, что в нем нет расширения, поэтому пустая строка на самом деле является правильным значением. Значение NULL будет правильным только в том случае, если вы не знаете, есть ли у него расширение или нет, или вы знали, что оно имеет, но не знали, что это такое. С такой ситуацией легче справиться в системах, где строковые значения являются нормой, потому что нет способа представить «пустое целое число», кроме вставки 0 или 1, которое будет свернуто при любых сравнениях, сделанных позже (которые имеют свои последствия) *.
Кстати, оба способа действительны в Postgres (поскольку мы обсуждаем «корпоративные» RDMBS), но результаты сравнения могут немного отличаться, когда вы добавляете NULL в смесь - потому что NULL == «не знаю», так что все результаты сравнения, включающего NULL, заканчиваются тем, что NULL, поскольку вы не можете знать то, что неизвестно. ОПАСНОСТЬ! Тщательно подумайте об этом: это означает, что результаты сравнения NULL распространяются через серию сравнений. Это может быть источником незаметных ошибок при сортировке, сравнении и т. Д.
Postgres предполагает, что вы взрослый человек, и можете принять это решение самостоятельно. Oracle и DB2 предполагают, что вы не поняли, что делаете что-то глупое, и выдавали ошибку. Это , как правило , правильно, но не всегда - вы , возможно , на самом деле не знаю , и имеют значение NULL в некоторых случаях и , следовательно , оставляя строку с неизвестным элементом , против которого содержательные сравнения невозможны это правильное поведение.
В любом случае вы должны стремиться исключить количество полей NULL, разрешенных для всей схемы, и вдвойне, если это касается полей, являющихся частью первичного ключа. В подавляющем большинстве случаев наличие столбцов NULL является признаком ненормализованной (в отличие от преднамеренно ненормализованной) схемы, и о ней следует очень тщательно подумать, прежде чем ее принять.
[* ПРИМЕЧАНИЕ. Можно создать пользовательский тип, представляющий собой объединение целых чисел и «нижнего» типа, который будет семантически означать «пустой», а не «неизвестный». К сожалению, это вносит некоторую сложность в операции сравнения, и, как правило, правильность ввода текста на практике не стоит усилий, так как вам вообще не следует разрешать много
NULL
значений. Тем не менее, было бы замечательно, если бы СУБД включалиBOTTOM
тип по умолчанию в дополнение кNULL
предотвращению привычки случайно связывать семантику «нет значения» с «неизвестным значением». ]источник
NULL == NULL -> false (по крайней мере, в СУБД)
Таким образом, вы не сможете получить какие-либо отношения, используя значение NULL, даже с дополнительными столбцами с реальными значениями.
источник
where pk_1 = 'a' and pk_2 = 'b'
обычные значения и переключаться,where pk_1 is null and pk_2 = 'b'
когда есть нули.where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))
/Ответ Тони Эндрюса приличный. Но реальный ответ заключается в том, что это соглашение использовалось сообществом реляционных баз данных и НЕ является необходимостью. Может быть, это хорошая конвенция, а может и нет.
Сравнение чего-либо со значением NULL приводит к НЕИЗВЕСТНОМУ (третье значение истинности). Таким образом, как было предложено с нулями, вся традиционная мудрость относительно равенства уходит в окно. Ну вот как это кажется на первый взгляд.
Но я не думаю, что это обязательно так, и даже базы данных SQL не считают, что NULL уничтожает все возможности для сравнения.
Запустите в вашей базе данных запрос SELECT * FROM VALUES (NULL) UNION SELECT * FROM VALUES (NULL)
Вы видите только один кортеж с одним атрибутом, который имеет значение NULL. Таким образом, объединение признало здесь два значения NULL равными.
При сравнении составного ключа, который имеет 3 компонента, с кортежем с 3 атрибутами (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 AND 3 = 3 AND NULL = NULL Результатом этого является UNKNOWN ,
Но мы могли бы определить новый тип оператора сравнения, например. ==. X == Y <=> X = Y ИЛИ (X НЕДЕЙСТВИТЕЛЬНО, И Y НУЛЬ)
Наличие такого типа оператора равенства сделает составные ключи с нулевыми компонентами или несоставные ключи с нулевым значением беспроблемными.
источник
Я все еще верю, что это фундаментальный / функциональный недостаток, вызванный техническими особенностями. Если у вас есть необязательное поле, с помощью которого вы можете идентифицировать клиента, теперь вам нужно ввести в него фиктивное значение только потому, что NULL! = NULL, не особенно элегантно, хотя и является «отраслевым стандартом».
источник