Как я могу избежать NULL в моей базе данных, также представляя недостающие данные? - PullRequest
29 голосов
/ 02 декабря 2010

В SQL и реляционной теории (CJ Date, 2009) глава 4 рекомендует избегать дублирования строк, а также избегать атрибутов NULL в данных, которые мы храним.Несмотря на то, что у меня нет проблем избежать дублирования строк, я изо всех сил пытаюсь понять, как я могу моделировать данные, не используя NULL.Возьмем, к примеру, следующее - это немного из работы.

У нас есть таблица artist, в которой, среди прочих столбцов, есть столбец gender.Это внешний ключ к таблице gender.Однако для некоторых артистов мы не знаем их пола - например, нам дали список новой музыки, в которой нет описания артиста.Как без использования NULL можно представить эти данные?Единственное решение, которое я вижу, - это добавить новый пол, «неизвестный», к таблице gender.

Хотя я очень наслаждаюсь этой книгой, я был очень разочарован, когда глава завершилась:

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

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


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

SELECT s.sno, p.pno
  FROM s, p
 WHERE s.city <> p.city
    OR p.city <> 'Paris'

Теперь рассмотрим пример, где s.city - Лондон, а p.city - Париж.В данном случае Лондон <> Париж, поэтому запрос верен.Теперь возьмем случай, когда p.city - не Париж, а на самом деле xyz .В этом случае (Лондон <> xyz ) ИЛИ ( xyz <> Париж) также имеет значение True.Итак, учитывая любые данные - этот запрос верный.Однако, если xyz 'NULL', сценарий меняется.В этом случае оба эти выражения не являются ни True , ни False, на самом деле они Unknown.И в этом случае, поскольку результат неизвестен, вы не получите все возвращенные строки.

Переход от логики с 2 значениями к логике с 3 значениями может легко привести к появлению таких ошибок.Infact, я только что представил один на работе, который мотивировал этот самый пост.Я хотел, чтобы все строки, где type != 0 Однако это фактически совпадает с type == 0 OR type IS NULL, приводят к путанице.

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

Ответы [ 8 ]

44 голосов
/ 05 декабря 2010

Все говорят, и никто, кроме Дпортаса и Уолтера, даже не может понять вопрос. Итак, 95% людей в SO не понимают Нулевую проблему и чувствуют угрозу, потому что их базы данных полны нулей, они хотят преобразовать искателя. Бесценный. Как кто-нибудь собирается учиться, когда они спорят?

Хорошо для вас, для устранения Nulls. Я никогда не допускал значения Nulls ни в одной из моих баз данных.

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

На самом деле это совсем не так сложно. Есть три варианта.

  1. Вот статья о Как справиться с пропавшим без вести Информация без использования N Darll от H Darwen, это может помочь вам разобраться в проблеме.

1,1. Шестая Нормальная Форма - это ответ. Но вам не нужно нормализовать вашу всю базу данных до 6NF. Для каждого необязательного столбца вам нужна дочерняя таблица вне основной таблицы, только с PK, который также является FK, потому что это отношение 1 :: 0-1. Кроме PK, единственным столбцом является необязательный столбец.

Посмотрите на это Модель данных ; AssetSerial на стр. 4 - классический случай: не все Assets имеют SerialNumbers; но когда они это сделают, я хочу, чтобы они сохранили их; более важно, я хочу убедиться, что они уникальны.

(Между прочим, для OO-людей это трехуровневая диаграмма классов в реляционной нотации, "наследование таблиц Concwete", ничего страшного, у нас это было 30 лет.)

* 1 035 * 1.2. Для каждой такой таблицы используйте View для предоставления формы таблицы 5NF. Конечно, используйте Null (или любое значение, подходящее для столбца), чтобы определить отсутствие столбца для любой строки. Но не обновлять через вид.

1.3 Не используйте прямые соединения для захвата столбца 6NF. Также не используйте внешние объединения (и сделайте так, чтобы сервер заполнил Null для пропущенных строк). Используйте подзапрос, чтобы заполнить столбец, и укажите значение, которое вы хотите вернуть для отсутствующего значения (кроме случаев, когда у вас есть Oracle, потому что его обработка подзапроса еще хуже, чем его обработка набора). Например. и просто например. Вы можете преобразовать числовой столбец в строку и использовать «Missing» для пропущенных строк.

Если вы не хотите заходить так далеко (6NF), у вас есть еще два варианта.
.
2. Вы можете использовать нулевые заменители. Я использую CHAR (0) для символьных колонн и 0 для числовых. Но я не допускаю этого для ФК. Очевидно, вам нужно значение, которое находится за пределами нормального диапазона данных. Это не позволяет использовать трехзначную логику.
.
3. В дополнение к (2) для каждого столбца Nullable необходим логический индикатор. Для примера столбца Sex индикатор будет выглядеть примерно так: SexIsMissing или SexLess (извините). Это позволяет очень жесткую трехзначную логику. Многим людям в этих 5% нравится, потому что дБ остается на 5NF (и меньше таблиц); столбцы с отсутствующей информацией загружаются со значениями, которые никогда не используются; они используются только в том случае, если индикатор имеет значение false. Если у вас есть корпоративная база данных, вы можете заключить ее в функцию и всегда использовать UDF, а не необработанный столбец.

Конечно, во всех случаях вы никогда не сможете избежать написания кода, необходимого для обработки недостающей информации. Будь то ISNULL(), или подзапрос для столбца 6NF, или Индикатор для проверки перед использованием значения, или UDF.

Если значение Null имеет конкретное значение ... , тогда оно не равно Null! По определению, Null - это неизвестное значение.

19 голосов
/ 04 декабря 2010

Так как же вы проектируете без NULLS? Это был оригинальный вопрос.

На самом деле это довольно просто. Вы разрабатываете так, что всякий раз, когда вам нужно оставить некоторые данные отсутствующими, вы можете сделать это, оставив всю строку отсутствующей. Если нет строки, это не строка, полная NULL. Это просто не там.

Итак, в случае «DateOfDeath» у нас есть таблица с двумя столбцами, а именно PersonId и DateOfDeath. PersonId ссылается на Id в таблице Persons. Если нет DateOfDeath для сохранения, мы не храним строку. Конец обсуждения.

Если вы выполните OUTER JOIN между этой таблицей и таблицей Persons, вы получите NULL для DateOfDeath везде, где не было строки. И если вы используете это в предложении where, вы получите обычное недоумение в отношении 3-значной логики. Если вы выполните INNER JOIN, строки, для которых нет DateOfDeath, просто исчезнут из объединения.

Конструкция, которая позволяет каждому столбцу быть принудительно НЕ ПУСТО (NULL), называется шестой нормальной формой.

Сказав все это, я часто допускаю значения NULL в некритических столбцах. И у меня нет краткого способа объяснить вам, как я определяю, что столбец является критическим.

7 голосов
/ 02 декабря 2010

Проще говоря, сохраняя только известную информацию - другими словами, предположение о закрытом мире.Постарайтесь быть в форме Бойса Кодда / Пятой Нормальной формы, и вы не ошибетесь.

4 голосов
/ 02 декабря 2010

null s являются следствием теории, отвечающей реальности, и должны быть скорректированы для использования.По моему мнению, попытка избежать всех значений null в конечном итоге приведет к более уродливому и менее поддерживаемому коду, чем просто использование null, где это уместно.

2 голосов
/ 02 декабря 2010

Требуются значения NULL - нет необходимости их заменять

Определение NULL в целом состоит в том, что его неизвестно - простая замена этого на произвольный тип делает то же самое, так почему же?

Для комментариев ниже:

Только что попробовал - ни то, ни другое не верно:

declare @x char
set @x = null

if @x = @x
begin
select 'true'
end

if @x <> @x
begin
select 'false'
end

Я могу только принять это, чтобы означать, что, поскольку нуль неизвестен, тогда он может 'нельзя сказать, что оно равно или не равно - следовательно, оба утверждения ложны

1 голос
/ 02 декабря 2010

NULL можно / нужно использовать до тех пор, пока:

A ) У вас есть деловая причина.Например, в таблице платежей значение платежа NULL будет означать, что оно никогда не выплачивалось.Значение 0.00 означает, что мы намеренно ничего не заплатили.Для медицинских карт значение NULL для показания артериального давления будет означать, что вы не принимаете АД, значение 0 будет означать, что пациент умер.Это существенное различие, и оно необходимо в некоторых приложениях.

B ) Ваши запросы объясняют это.Если вы понимаете влияние NULL на IN, EXISTS, операторов неравенства (как вы указали в OP) и т. Д., Тогда это не должно быть проблемой.Если у вас есть NULL сейчас в ваших таблицах и вам не нужно значение для определенных приложений, вы можете использовать представления и либо COALESCE, либо ISNULL для заполнения разных значений, если исходная таблица имеет NULL.

РЕДАКТИРОВАТЬ:

Чтобы ответить на вопросы OP о неравенстве / равенстве "реального мира", используя NULL, это отличный пример, который я иногда использую.Вы на вечеринке с 3 другими людьми.Вы знаете, что одного человека зовут «Джон», но других не знаете.

Логически , ответ на вопрос «Сколько людей зовут Джо» неизвестен или NULL.В SQL это будет что-то вроде

SELECT name FROM party where NAME = 'Joe' Вы не получите строк, поскольку не знаете их имен.Они могут быть или не быть Джо.

Ваше неравенство будет:

SELECT name from party where NAME <> 'Joe' Вы получите только возвращаемое значение для «Джона», поскольку имя Джона - это все, что вы знаете.Другие люди могут быть или не быть Джо, но у вас нет возможности узнать.

0 голосов
/ 03 декабря 2010

Не допускайте, чтобы столбец определялся как NULL, если это вообще возможно.Для меня это не имеет ничего общего с бизнес-правилом, которое означает, что NULL означает, что оно связано с дисковым вводом-выводом.

В SQL Server столбец, допускающий значение NULL, скажем, символ 10, будет приниматьодин бит в битовой карте, когда ноль, и 10 байтов, когда не обнуляется.Так как же это сделать с нулевым повреждением дискового ввода-вывода.Это причиняет боль, когда значение вставляется в столбец, где раньше был ноль.Поскольку SQL не зарезервировал пространство, в строке нет места, чтобы просто поместить значение, поэтому SQL Server должен перемещать данные, чтобы освободить место.Разделение страниц, фрагментация, обновление RID, если это HEAP, и т. Д. Все операции ввода-вывода с поврежденными дисками.

Кстати, если есть гендерная таблица, мы можем добавить еще одну строку для «Невозможно определить истинное сексуальное происхождение илисостояние личности ".

0 голосов
/ 02 декабря 2010

Я не согласен с автором и утверждаю, что NULL на самом деле является ПРАВИЛЬНЫМ способом обработки пропущенных данных для необязательных полей.Фактически, это причина того, что NULL существует вообще ...

Для вашей конкретной проблемы, касающейся пола:

  • Вы уверены, что хотите таблицу пола и понесете расходы надополнительное объединение для каждого запроса?Для простых перечисляемых типов вполне разумно сделать поле целым и определить 1 = мужской, 2 = женский, NULL = неизвестный.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...