Допускается NULL в первичном ключе - почему и в какой СУБД? - PullRequest
19 голосов

В дополнение к моему вопросу «Почему в TSQL использовать« не нулевой первичный ключ »?» ...

Как я понял из других обсуждений, некоторые СУБД (например, SQLite, MySQL ) разрешают "уникальный" NULL в первичном ключе.

Почему это разрешено и чем оно может быть полезно?

Справочная информация. Я считаю, что для общения с коллегами и специалистами по базам данных полезно знать различия в фундаментальных концепциях, подходах и их реализациях в различных СУБД.

Примечания

  • MySQL восстановлен и возвращен в список «NOT NULL PK».
  • SQLite был добавлен (благодаря Полу Хэдфилду) в список "NULL PK":

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

Если оператор INSERT или UPDATE пытается изменить содержимое таблицы, чтобы две или более строки имели одинаковые значения первичного ключа, это является нарушением ограничения. Согласно стандарту SQL, PRIMARY KEY всегда должен подразумевать NOT NULL. К сожалению, из-за давнего надзора за кодированием в SQLite это не так.

Если столбец не является INTEGER PRIMARY KEY SQLite допускает значения NULL в столбце PRIMARY KEY . Мы могли бы изменить SQLite, чтобы он соответствовал стандарту (и мы могли бы сделать это в будущем), но к тому времени, когда был обнаружен недосмотр, SQLite стал настолько широко использоваться, что мы боялись нарушать унаследованный код, если решим проблему.

Так что на данный момент мы решили продолжить разрешать NULL в столбцах PRIMARY KEY. Разработчики должны знать, однако, что мы можем изменить SQLite для соответствия стандарту SQL в будущем и должны разрабатывать новые программы соответствующим образом.

- SQL как понял SQLite: CREATE TABLE

Ответы [ 6 ]

26 голосов
/ 11 октября 2010

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

Если вы хотите, чтобы вторая строка была отклонена на том основании, что во второй строке Kn имеет значение null, а таблица уже содержит строку с Kn null, то вам фактически требуется, чтобы система обработала сравнение "row1". Kn = row2.Kn "в качестве значения TRUE (потому что вы так или иначе хотите, чтобы система обнаружила, что значения ключей в этих строках действительно равны). Однако это сравнение сводится к сравнению "null = null", и стандарт уже явно указывает, что null не сравнивается ни с чем, в том числе и с самим собой.

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

5 голосов
/ 11 октября 2010

Я не знаю, отличаются ли старые версии MySQL от этого, но в современных версиях первичный ключ должен быть в столбцах, которые не равны NULL.См. страницу руководства по CREATE TABLE: «A PRIMARY KEY - это уникальный индекс, где все ключевые столбцы должны быть определены как NOT NULL. Если они явно не объявлены как NOT NULL, MySQL объявляет ихтак безоговорочно (и тихо). "

4 голосов
/ 11 октября 2010

Что касается теории реляционных баз данных:

  • Первичный ключ таблицы используется для уникальной идентификации каждой строки в таблице
  • Значение NULL в столбце означает, что вы не знаете, что это за значение
  • Поэтому никогда не следует использовать значение «Я не знаю», чтобы однозначно идентифицировать строку в таблице.

В зависимости от данных, которые вы моделируете, вместо NULL может быть использовано «подготовленное» значение. Я использовал 0, «N / A», «1 января 1980» и аналогичные значения, чтобы представить фиктивные «данные, которые, как известно, отсутствуют».

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

3 голосов
/ 11 октября 2010

Ну, это может позволить вам реализовать Null Object Pattern изначально в базе данных.Так что, если вы использовали нечто подобное в коде, который очень тесно взаимодействовал с БД, вы могли бы просто найти объект, соответствующий ключу, без особой проверки нуля.

Теперь, стоит ли этоФункциональность Я не уверен, но на самом деле вопрос в том, перевешивают ли плюсы запрета на использование нулевых ключей в абсолютно всех случаях против того, чтобы помешать кому-то, кто (к лучшему или худшему) на самом деле хочет использовать нулевые ключи.Это будет стоить того, только если вы сможете продемонстрировать некоторое нетривиальное улучшение (например, более быстрый поиск ключей), если сможете гарантировать, что ключи не равны NULL.Некоторые движки БД показывают это, другие - нет.И если нет никаких настоящих плюсов от форсирования этого, зачем искусственно ограничивать ваших клиентов?

1 голос
/ 21 сентября 2016

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

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

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

AnПример сценария, чтобы уточнить это: у меня есть таблица users.Поскольку я требую, чтобы у каждого пользователя было отдельное имя пользователя, я решил использовать username в качестве первичного ключа.Я хочу поддерживать удаление пользователей, но так как мне необходимо исторически отслеживать существование пользователей для целей аудита, я использую мягкое удаление (в первой версии схемы я добавляю флаг «удаленный» для пользователя и гарантирую, что удаленныйфлаг отмечен во всех запросах, где ожидаются только активные пользователи).

Однако дополнительное требование заключается в том, что если имя пользователя удалено, оно должно быть доступно для регистрации новым пользователям.Привлекательным способом для достижения этой цели было бы изменить флаг удаления на временную метку, которая может иметь значение NULL (где нули указывают, что пользователь не был удален) и поместить ее в первичный ключ.Если бы первичные ключи допускали пустые столбцы, это имело бы следующий эффект:

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

Однако на самом деле этого нельзя достичь стандартным SQL, поэтому вместо этогонеобходимо использовать другой первичный ключ (возможно, сгенерированный числовой идентификатор пользователя в этом случае) и использовать ограничение UNIQUE для обеспечения уникальности (username, deleted).

0 голосов
/ 14 июля 2016

Наличие нулевого первичного ключа может быть полезным в некоторых сценариях.В одном из моих проектов я использовал эту функцию во время синхронизации баз данных: одну на сервере и много на устройствах разных пользователей.Учитывая тот факт, что не все пользователи имеют доступ к Интернету все время, я решил, что только основная база данных сможет идентифицировать мои объекты.SQLite имеет собственный механизм нумерации строк.Если бы я использовал дополнительное поле id, я бы использовал больше полосы пропускания.Наличие нулевого идентификатора в качестве идентификатора не только уведомляет меня о том, что объект был создан на клиентском устройстве, когда у него не было доступа к Интернету, но также уменьшает сложность кода.Единственным недостатком является то, что на клиентском устройстве я не могу получить объект по его идентификатору, если он не был предварительно синхронизирован с основной базой данных.Однако это не проблема, так как мой пользователь заботится о сущностях для их параметров, а не их уникальных идентификаторов.

...