Я больше занимаюсь веб-программированием, чем администрированием баз данных, поэтому, пожалуйста, исправьте меня, если я здесь использую неправильную терминологию. Я пытаюсь найти лучший способ создать базу данных для приложения, которое я буду кодировать.
Ситуация: у меня есть отчеты в одной таблице и рекомендации в другой таблице. Каждый отчет может иметь много рекомендаций. У меня также есть отдельная таблица для ключевых слов (для реализации тегов). Однако я хочу иметь только один набор ключевых слов, который будет применяться как к отчетам, так и к рекомендациям, чтобы поиск по ключевым словам давал вам отчеты и рекомендации в качестве результатов.
Вот структура, с которой я начал:
Reports
----------
ReportID
ReportName
Recommendations
----------
RecommendationID
RecommendationName
ReportID (foreign key)
Keywords
----------
KeywordID
KeywordName
ObjectKeywords
----------
KeywordID (foreign key)
ReportID (foreign key)
RecommendationID (foreign key)
Инстинктивно, я чувствую, что это не оптимально, и мне нужно, чтобы мои тегируемые объекты наследовали от общего родителя, и чтобы этот родительский комментарий был помечен, что дало бы следующую структуру:
BaseObjects
----------
ObjectID (primary key)
ObjectType
Reports
----------
ObjectID_Report (foreign key)
ReportName
Recommendations
----------
ObjectID_Recommendation (foreign key)
RecommendationName
ObjectID_Report (foreign key)
Keywords
----------
KeywordID (primary key)
KeywordName
ObjectKeywords
----------
ObjectID (foreign key)
KeywordID (foreign key)
Должен ли я пойти с этой второй структурой? Я пропускаю какие-то важные проблемы здесь? Кроме того, если я пойду со вторым, что я должен использовать в качестве неуниверсального имени вместо «Объект»?
Обновить:
Я использую SQL Server для этого проекта. Это внутреннее приложение с небольшим количеством не одновременно работающих пользователей, поэтому я не ожидаю высокой нагрузки. С точки зрения использования ключевые слова, вероятно, будут использоваться экономно. Это в основном только для целей статистической отчетности. В этом смысле, какое бы решение я ни использовал, оно, вероятно, повлияет только на разработчиков, которым необходимо поддерживать эту систему на низком уровне ... но я подумал, что это хорошо, когда я могу внедрять хорошие практики. Спасибо за все понимание!
источник
Ответы:
Проблема с вашим первым примером - таблица с тремя связями. Будет ли требоваться, чтобы один из внешних ключей в отчете или рекомендациях всегда был равен NULL, чтобы ключевые слова связывались только так или иначе?
В случае вашего второго примера теперь для соединения базы с производными таблицами может потребоваться использование селектора типа или LEFT JOINs в зависимости от того, как вы это делаете.
Учитывая это, почему бы просто не сделать это явным и исключить все значения NULL и LEFT JOIN?
В этом сценарии, когда вы добавляете что-то еще, что нужно пометить, вы просто добавляете таблицу сущностей и таблицу связей.
Тогда ваши результаты поиска будут выглядеть следующим образом (смотрите, что все еще происходит выбор типов и превращение их в универсальные на уровне результатов объекта, если вам нужен единый список результатов):
Неважно, что где-то там будет выбор типа и какое-то ветвление.
Если вы посмотрите на то, как вы это сделаете, в своем варианте 1, он похож, но с оператором CASE или LEFT JOINs и COALESCE. Поскольку вы расширяете свой вариант 2, добавляя больше связанных вещей, вы должны продолжать добавлять больше ЛЕВЫХ СОЕДИНЕНИЙ, где вещи обычно НЕ обнаруживаются (связанный объект может иметь только одну производную таблицу, которая является действительной).
Я не думаю, что с вашим вариантом 2 что-то принципиально не так, и вы могли бы фактически сделать его похожим на это предложение с использованием представлений.
В вашем варианте 1 мне трудно понять, почему вы выбрали таблицу с тремя ссылками.
источник
Во-первых, обратите внимание, что идеальное решение в некоторой степени зависит от того, какую СУБД вы используете. Я собираюсь дать как стандартный, так и специфичный для PostgreSQL ответ.
Нормализованный стандартный ответ
Стандартный ответ - две таблицы соединений.
Предположим, у нас есть наши таблицы:
Этот подход следует всем стандартным правилам нормализации и не нарушает традиционные принципы нормализации базы данных. Это должно работать на любой RDBMS.
PostgreSQL-специфичный ответ, дизайн N1NF
Во-первых, несколько слов о том, почему PostgreSQL отличается. PostgreSQL поддерживает ряд очень полезных способов использования индексов над массивами, в первую очередь с использованием так называемых индексов GIN. Они могут значительно повысить производительность, если использовать их правильно. Поскольку PostgreSQL может таким образом «охватить» типы данных, базовое предположение об атомарности и нормализации несколько проблематично применить здесь. Поэтому по этой причине я рекомендую нарушить правило атомарности первой нормальной формы и полагаться на индексы GIN для повышения производительности.
Второе замечание заключается в том, что, хотя это дает лучшую производительность, оно добавляет некоторые головные боли, потому что вам придется поработать вручную, чтобы заставить ссылочную целостность работать правильно. Таким образом, компромисс здесь - производительность для ручной работы.
Теперь нам нужно добавить триггеры, чтобы обеспечить правильное управление ключевыми словами.
Во-вторых, мы должны решить, что делать, когда ключевое слово удалено. В настоящее время ключевое слово, удаленное из таблицы ключевых слов, не будет каскадно добавляться к полям ключевых слов. Может быть, это желательно, а может и нет. Самое простое, что нужно сделать, это просто ограничить удаление всегда и ожидать, что вы вручную обработаете этот случай, если он возникнет (используйте триггер для безопасности здесь). Другой вариант может состоять в том, чтобы переписать каждое значение ключевого слова, в котором существует ключевое слово, для его удаления. Опять же, триггер был бы способом сделать это.
Большим преимуществом этого решения является то, что вы можете индексировать для очень быстрого поиска по ключевому слову, и вы можете извлекать все теги без объединения. Недостатком является то, что удаление ключевого слова является болью и не будет хорошо работать даже в хороший день. Это может быть приемлемо, потому что это редкое событие и может быть отправлено в фоновый процесс, но это компромисс, который стоит понять.
Критикуя ваше первое решение
Реальная проблема с вашим первым решением состоит в том, что у вас нет возможных ключей на ObjectKeywords. Следовательно, у вас есть проблема, когда вы не можете гарантировать, что каждое ключевое слово применяется к каждому объекту только один раз.
Ваше второе решение немного лучше. Если вам не нравятся другие предлагаемые решения, я бы предложил пойти на это. Тем не менее, я бы предложил избавиться от keyword_id и просто присоединиться к тексту ключевого слова. Это исключает соединение без денормализации.
источник
Я бы предложил две отдельные структуры:
Таким образом, у вас не будет всех возможных идентификаторов сущностей в одной и той же таблице (что не очень масштабируемо и может привести к путанице), и у вас нет таблицы с общим «идентификатором объекта», которую вы должны устранить в другом месте используя
base_object
таблицу, которая будет работать, но я думаю, что усложняет дизайн.источник
BaseObjects
таблицу при первом прочтении и подумал, что вижу описание таблицы, в которойobject_id
можно указать идентификатор в любой таблице.По моему опыту, это то, что вы можете сделать.
А для связи между ключевыми словами, отчетами и рекомендациями вы можете сделать один из двух вариантов: Вариант A:
Это позволяет установить прямую связь между отчетами, рекомендациями, ключевыми словами и, наконец, ключевыми словами. Вариант Б:
Вариант A проще применять и управлять, поскольку он будет иметь ограничения базы данных для обеспечения целостности данных и не позволит вставлять недопустимые данные.
Вариант B требует немного больше работы, так как вам нужно будет кодировать идентификацию отношений. Это более гибко в долгосрочной перспективе, если по какой-либо причине в будущем вам понадобится добавить ключевые слова к другому элементу, кроме отчета или рекомендации, вам просто нужно добавить идентификацию и использовать непосредственно таблицу.
источник