Варианты исключения столбцов NULLable из модели БД (во избежание трехзначной логики SQL)? - PullRequest
8 голосов
/ 20 июня 2010

Некоторое время назад я читал книгу SQL и теория отношений от CJ Date .Автор известен тем, что критиковал трехзначную логику SQL (3VL). 1)

Автор подчеркивает, почему следует избегать 3VL вSQL, однако он не описывает как будет выглядеть модель базы данных, если бы столбцы, допускающие значения NULL, не были разрешены .Я немного подумал над этим и нашел следующие решения.Если бы я пропустил другие варианты дизайна, я хотел бы услышать о них!

1) Критика Дейта по поводу 3VL в SQL тоже подверглась критике:см. эту статью Клода Рубинсона (включая оригинальную критику С.Дж. Дейта).


Пример таблицы:

AsВ качестве примера возьмем следующую таблицу, в которой у нас есть один столбец, допускающий значение NULL (DateOfBirth):

#  +-------------------------------------------+
#  |                   People                  |
#  +------------+--------------+---------------+
#  |  PersonID  |  Name        |  DateOfBirth  |
#  +============+--------------+---------------+
#  |  1         |  Banana Man  |  NULL         |
#  +------------+--------------+---------------+

Вариант 1: эмуляция NULL с помощью флага и значения по умолчанию:

Вместо того, чтобы сделать столбец обнуляемым, указывается любое значение по умолчанию (например, 1900-01-01).В дополнительном столбце BOOLEAN будет указано, следует ли просто игнорировать значение в DateOfBirth или оно действительно содержит данные.

#  +------------------------------------------------------------------+
#  |                              People'                             |
#  +------------+--------------+----------------------+---------------+
#  |  PersonID  |  Name        |  IsDateOfBirthKnown  |  DateOfBirth  |
#  +============+--------------+----------------------+---------------+
#  |  1         |  Banana Man  |  FALSE               |  1900-01-01   |
#  +------------+--------------+----------------------+---------------+

Вариант 2: Превращение обнуляемого столбца вотдельная таблица:

Обнуляемый столбец заменяется новой таблицей (DatesOfBirth).Если запись не содержит данных для этого столбца, в новой таблице не будет записи:

#  +---------------------------+ 1    0..1 +----------------------------+
#  |         People'           | <-------> |         DatesOfBirth       |
#  +------------+--------------+           +------------+---------------+
#  |  PersonID  |  Name        |           |  PersonID  |  DateOfBirth  |
#  +============+--------------+           +============+---------------+
#  |  1         |  Banana Man  |
#  +------------+--------------+

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


Вопрос: Есть ли другие варианты устранения NULL (и если да, то каковы они)?

Ответы [ 7 ]

4 голосов
/ 20 июня 2010

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

Его решение - вариант вашего второго подхода. Это шестая нормальная форма с таблицами для хранения даты рождения и идентификаторов, где она неизвестна:

#  +-----------------------------+ 1    0..1 +----------------------------+
#  |         People'             | <-------> |         DatesOfBirth       |
#  +------------+----------------+           +------------+---------------+
#  |  PersonID  |  Name          |           |  PersonID  |  DateOfBirth  |
#  +============+----------------+           +============+---------------+
#  |  1         |  Banana Man    |           ! 2          | 20-MAY-1991   |
#  |  2         |  Satsuma Girl  |           +------------+---------------+
#  +------------+----------------+
#                                  1    0..1 +------------+
#                                  <-------> | DobUnknown |
#                                            +------------+
#                                            |  PersonID  |
#                                            +============+
#                                            | 1          |
#                                            +------------+

Чтобы выбрать «Люди», необходимо объединить все три таблицы, включая шаблон, чтобы указать неизвестные даты рождения.

Конечно, это несколько теоретически. Состояние SQL в наши дни еще недостаточно развито, чтобы справиться со всем этим. Презентация Хью покрывает эти недостатки. Одна вещь, которую он упоминает, не совсем верна: некоторые разновидности SQL поддерживают множественное присваивание - например, Синтаксис Oracle INSERT ALL .

2 голосов
/ 07 сентября 2011

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

Так как НАРУЖНЫЕ СОЕДИНЕНИЯ не будут разрешены (потому что они вводят NULL в набор результатов), все необходимые данные больше не могли получать с помощью одного запроса, как и раньше.

… это не тот случай, но я согласен с тем, что проблема внешнего объединения явно не упоминается в статье Дарвена; это было единственное, что оставило меня желать. Четкий ответ можно найти в другой книге Дейта ...

Во-первых, обратите внимание, что Date и собственный действительно реляционный язык Дарвена Tutorial D имеет только один тип соединения, являющийся естественным соединением. Обоснование состоит в том, что на самом деле нужен только один тип соединения.

Книга данных, на которую я ссылался, является превосходной SQL и реляционная теория: как написать точный код SQL :

4.6: Замечание по внешнему объединению: «Говоря языком, [внешнее объединение] вид брака с дробовиком: он превращает столы в своего рода союз - да, я означает объединение, а не объединение, даже если рассматриваемые таблицы не соответствовать обычным требованиям для союза ... Это делает это, в эффект, добавив одну или обе таблицы с нулями перед выполнением союз, тем самым заставляя их соответствовать этим обычным требованиям в конце концов. Но нет никаких причин, почему это заполнение не должно быть сделано с правильными значениями вместо нулей

Используя ваш пример и значение по умолчанию '1900-01-01' в качестве 'padding', альтернатива внешнему объединению может выглядеть так:

SELECT p.PersonID, p.Name, b.DateOfBirth
  FROM Person AS p
       INNER JOIN BirthDate AS b
          ON p.PersonID = b.PersonID
UNION
SELECT p.PersonID, p.Name, '1900-01-01' AS DateOfBirth
  FROM Person AS p
 WHERE NOT EXISTS (
                   SELECT * 
                     FROM BirthDate AS b
                    WHERE p.PersonID = b.PersonID
                  );

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

Обратите внимание, что в вышеприведенном случае используются JOIN и INNER JOIN только потому, что стандартные SQL-92 NATURAL JOIN и UNION CORRESPONDING не получили широкого применения в реальных продуктах SQL (не могу найти цитату, но IIRC Darwen был в основном ответственен последние два превращаются в Стандарт).

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

Person JOIN BirthDate UNION Person NOT MATCHING BirthDate ADD '1900-01-01' AS DateOfBirth;
1 голос
/ 20 июня 2010

Я не читал его, но есть статья под названием Как обрабатывать недостающую информацию с помощью S-by-C на веб-сайте Third Manifesto , который ведет Хью Дарвен и CJ Date. Это не написано С.Дж. Дейтом, но я предполагаю, что, поскольку это одна из статей на этом сайте, она, вероятно, похожа на его мнение.

0 голосов
/ 31 мая 2013

Одна из опций - это использование явных типов опций , аналогично Maybe функтору Haskell.

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

Это возвращает своего рода «ноль» только для тех атрибутов, для которых вы явно запрашиваете это, но без глупой трехзначной логики null. Nothing == Nothing равно True, а не unknown или null.

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

data EmploymentStatus = Employed EmployerID | Unemployed | Unknown

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

Если не считать этого, я согласен с APC и onedaywhen в ответах о 6NF.

0 голосов
/ 07 сентября 2011

Вы также можете исключить null в выводе, используя COALESCE.

SELECT personid  /*primary key, will never be null here*/
       , COALESCE(name, 'no name') as name
       , COALESCE(birthdate,'no date') as birthdate
FROM people

Не все базы данных поддерживают COALESCE, но почти все имеют запасную опцию под названием
IFNULL(arg1, arg2) или что-то похожее, что будет делать то же самое (но только для 2 аргументов) .

0 голосов
/ 22 июня 2010

Вариант 3: ответственность за запись:

CREATE TABLE Person
(
  PersonId int PRIMARY KEY IDENTITY(1,1),
  Name nvarchar(100) NOT NULL,
  DateOfBirth datetime NOT NULL
)

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

0 голосов
/ 20 июня 2010

Одной альтернативой может быть модель entity-attribute-value :

 entity  attribute    value
 1       name         Banana Man
 1       birthdate    1968-06-20

Если дата рождения была неизвестна, вы просто пропустите ее строку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...