Проектирование базы данных: гибкое хранение вопросов и ответов

#database-design #relational-database

#проектирование базы данных #реляционная база данных

Вопрос:

Я давно не проектировал базу данных, и сейчас у меня нет большой уверенности в своем дизайне. У меня в основном есть три таблицы в базе данных, которые представляют своего рода историю. Я должен изменить эту систему, чтобы каждый раз, когда добавляется запись (например, что-то становится историческим), от пользователя мог потребоваться некоторый ввод. Иногда у них будет всего один простой вопрос, в других случаях от них потребуется предоставить пять различных фрагментов информации, и конечный пользователь должен гибко управлять ими через административный интерфейс. Итак, я планирую создать таблицу вопросов и таблицу ответов со составными таблицами, чтобы связать их с тремя другими таблицами. Я испытываю трудности с дизайном таблицы ответов, потому что для каждого вопроса может потребоваться множество ответов. Некоторые ответы будут выпадающими вариантами, которые привязаны к другой базе данных через внешний ключ. Другими могут быть ввод текста, даты или ответы «Да» / «Нет». Прямо сейчас у меня есть таблица ответов, которая охватывает все типы ответов с рядом полей, в которых можно указать значение null…

  ---------------------------- 
| Answer                     |
 ---------------------------- 
| Id (int)                   |
| QuestionId (int)           |
| ForeignKeyId1* (int)       |
| ForeignKeyId2* (int)       |
| ForeignKeyId3* (int)       |
| Number* (bigint)           |
| DateField* (date)          |
| Text* (varchar 500)        |
| YesNo* (bit)               |
 ---------------------------- 
  *Nullable
  

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

Это плохой дизайн? Что могло бы сделать это лучше?

Ответ №1:

Из того, что вы описали, ваше решение выглядит так, как будто оно должно работать нормально. Я выполнил аналогичные настройки с таблицами, столбцы которых настроены для записи определенных типов данных, позволяя другим полям обнуляться. На мой взгляд, выглядит неплохо, хотя я не уверен, для чего предназначены ваши поля ForeignKeyID :

Комментарии:

1. Поля ForeignKeyId предназначены для ответов, которые пользователь выберет из выпадающего списка. Они привязаны к другим таблицам. Спасибо за ваш отзыв 🙂

2. Ну ладно. Не хочу вдаваться в подробности того, что вы пытаетесь сделать, но не рассматривали ли вы возможность создания другой таблицы идентификаторов ответов и вопросов. Таким образом, вы могли бы получить столько правильных ответов, сколько хотели. В настоящее время вы столкнулись бы с проблемами, если бы захотели добавить другой правильный ответ на любой вопрос. Просто предложение 🙂 Надеюсь, все пройдет хорошо.

3. Это уже реализовано, но на самом деле это не имеет отношения к рассматриваемой проблеме 😉

Ответ №2:

Наличие разных полей для разных типов данных — это нормально, как и наличие нескольких внешних ключей. Этот дизайн максимально обеспечивает целостность домена данных в СУБД. С философской точки зрения это хороший способ, поскольку большинство людей скажут вам, что, позволив DMBS выполнять всю работу за вас, вы сэкономите на написании и поддержке кода. Большую часть времени я был бы одним из таких людей.

Есть другой способ взглянуть на это с точки зрения ремонтопригодности. В нынешнем виде вам придется вернуться назад и изменить структуру вашей таблицы ОТВЕТОВ, если вы добавите либо новый тип данных, например, число с плавающей запятой, либо идентификатор GUID. Аналогично, вам придется вернуться назад и добавить другое поле FK, если вы задаете другой тип вопроса, для которого требуется ответ из новой таблицы поиска.

Вы могли бы изменить свою таблицу ОТВЕТОВ, чтобы использовать универсальный подход, чтобы она выглядела более похоже на это:

 ANSWER
( Id (int)
, QuestionId (int)
, Part (int)
, Value (nvarchar 1000)
)
  

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

Итак, почему вы могли бы отказаться от предоставления базе данных возможности обеспечивать целостность домена данных? Вот относительные плюсы и минусы:

Преимущества множества отдельных полей для ответов:

  • СУБД не позволит вам помещать данные неправильного типа в поле, поэтому вам не нужно писать или вызывать какие-либо функции целостности домена данных перед сохранением данных (если вы не хотите перехватывать эти ошибки на уровне графического интерфейса)
  • Нет никакой путаницы в том, как интерпретировать содержимое ответа (например, какой месяц и какой день в этом ответе с датой …)

Преимущества универсального поля для ответов:

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

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

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

Комментарии:

1. Большое вам спасибо за предоставленный ответ!