Хитрый вопрос проектирования реляционных БД - PullRequest
2 голосов
/ 11 февраля 2009

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

Это мои таблицы:

  • сайт
  • site_node
  • страница

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

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

  1. поля parentType и parentId в таблице страниц, где типом может быть либо «узел», «site_error» или «site_notFound», а идентификатором будет идентификатор сайта или узла (в зависимости от того, что имеет отношение к типу).
  2. поле nodeId в таблице страниц, которое может быть пустым, а затем поля errorPageId и notFoundPageId в таблице сайта.

Опция # 1 гарантирует, что каждая страница принадлежит одному и только одному другому объекту, хотя на самом деле связь не может быть применена, поскольку поле parentId может указывать на более чем одно место.

Вариант №2 чище, но в основном говорится, что сайт "принадлежит" двум ошибкам и не найденным страницам, и это, вероятно, плохая практика.

Есть мысли или предложения?
Спасибо,
Jack

Ответы [ 5 ]

3 голосов
/ 11 февраля 2009

Сделать фиктивные узлы сайта для страницы с ошибкой или не найдена. Вы можете пометить их как определенный тип узла в соответствии с вашим первым вариантом. Это облегчит создание универсального механизма обработчика. Это также упростит объединение, что поможет повысить производительность запросов к базе данных. Кроме того, он позволяет добавлять дополнительные типы «специальных» страниц (возможно, экран входа в систему) или настраивать их без необходимости изменения схемы базы данных.

2 голосов
/ 11 февраля 2009

Опция № 1 гарантирует, что каждая страница принадлежит одному и только одному другому сущность, хотя отношения не могу на самом деле должен быть принудительно установлен в качестве parentId поле может указывать на несколько место.

правый. С точки зрения теории отношений проблема заключается в том, что ваш столбец "parentId" нарушает третью нормальную форму , поскольку его значение варьируется в зависимости от значения строки в parentType (столбец без ключа) .

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

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

Вариант №2 чище, но это в основном говорят, что сайт «принадлежит» двум ошибкам, а не найденные страницы, и это, вероятно, плохо практика.

Я понимаю, почему вы говорите, что из-за относится к соглашениям в Rails-подобных фреймворках. Но это условности; они не обязательно являются единственными отношениями, которые могут моделировать внешние ключи. Вы можете сделать так, чтобы одна сущность ссылалась ровно на одну другую сущность, в есть одна связь. В этом случае внешний ключ меняет направление.

Я бы сказал, что логически верно, что страница Error и страница Not Found принадлежат сайту, а не наоборот. И способ сделать их обязательными состоит в том, чтобы другой объект ссылался на эти страницы и применял ограничение NOT NULL к этим ссылкам. Это то, что вы описали.

CREATE TABLE site (
  . . .
  error_page_id     INT NOT NULL,
  notfound_page_id  INT NOT NULL,
  FOREIGN KEY (error_page_id)    REFERENCES pages (page_id),
  FOREIGN KEY (notfound_page_id) REFERENCES pages (page_id)
);

Это отвечает вашим насущным потребностям, подлежит исполнению и находится в обычной форме.


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

@ Тони Эндрюс предлагает сохранить два столбца на каждой странице site_id и site_node_id и добавить ограничение CHECK, чтобы гарантировать, что один из них не равен NULL. Кажется, это лучше, чем опция parent_id / parent_type, но все равно не предлагает каких-либо принудительных мер, что на каждом сайте должна быть страница об ошибке и страница не найдена.

0 голосов
/ 11 февраля 2009

Модификация варианта 1.

Включите два отдельных столбца, ParentNodeID и ParentSiteID. Оставьте один из этих двух столбцов NULL, в зависимости от случая. Теперь вы все еще можете объявить ограничение внешнего ключа (ссылок) для каждого внешнего ключа.

Я не совсем понимаю дело SiteNotFound. Не могли бы вы оставить оба внешних ключа NULL в этом случае?

Ваши объединения и поиски будут проще. Вы также будете придерживаться 1NF. Это не совпадение.

Ваш вариант 1 объединяет значения, полученные из разных доменов, в одном поле. Это плохой дизайн поля, и IIRC нарушает 1NF.

0 голосов
/ 11 февраля 2009

Вариант 2 имеет гораздо больше смысла и спасет ваш мозг в дальнейшем, если и когда возникнут дополнительные осложнения. Отношение «один к одному» сайта с ошибкой / не найденной страницей делает его идеальным для ограничения внешнего ключа.

0 голосов
/ 11 февраля 2009

Другой вариант - иметь 2 столбца site_id и site_node_id, например:

create table pages
 ( page_id ... primary key
 , site_id references sites
 , site_node_id references site_nodes
 , ...
 , constraint site_or_node check (  site_id is null and site_node_id is not null
                                 or site_id is not null and site_node_id is null
                                 )
 );

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

...