Нормализованный, денормализованный или как?Таблица для хранения ответов в базе данных опроса - PullRequest
1 голос
/ 04 марта 2012

Я намеренно формулирую это как общий вопрос, хотя я использую ORM Джанго для реализации этих таблиц. Вот мои модели Django , если вы хотите взглянуть.

Загадка

У меня есть несколько таблиц со следующими отношениями:

Survey <--m2o-- Page <--m2o-- Category <--m2o-- Question <--m2o-- Choice <--?-- Response
|               |             |                 |                 |
|- name         |-number      |- name           |- sortid         |- sortid
                              |- sortid         |- text           |- text
                              |- text           |- short
                                                |- qtype

Где m2o - это сокращение от «многие к одному», у каждой таблицы есть неявный первичный ключ, и мой вопрос касается того, как организовать таблицу ответов.

Таблица ответов должна иметь какое-то отношение к таблице пользователя (в зависимости от определения таблицы это может быть один к одному или много к одному). Цель таблицы - хранить ответы пользователей на конкретные вопросы. Проблема, с которой я столкнулся, заключается в том, что в зависимости от типа вопроса мне нужно хранить ответ в столбце другого типа в таблице ответов. Например, таблица может напоминать:

| user | (question) | (qtype)         | choice    | response                      |
|------+------------+-----------------+-----------+-------------------------------|
| 1    | Q1         | Select One      | A         | False                         |
| 1    | ''         | ''              | B         | False                         |
| 1    | ''         | ''              | C         | True                          |
| 1    | ..         | ''              | D         | False                         |
| 1    | Q2         | Select Multiple | 1         | True                          |
| ''   | ''         | ''              | 2         | True                          |
| ''   | ''         | ''              | 3         | False                         |
| ''   | ''         | ''              | 4         | True                          |
| ''   | Q3         | Long Text       | NULL      | "It was the best of times..." |
| ''   | Q4         | Select Explain  | A         | False                         |
| ''   | ''         | ''              | B         | False                         |
| ''   | ''         | ''              | C         | False                         |
| ''   | ''         | ''              | D (other) | True                          |
| ''   | ''         | ''              | Explain   | "I actually prefer bananas."  |

Я добавил столбцы для ясности, которые бы неявно определялись отношениями и поэтому не присутствовали в реальной таблице. Они обозначены ( ). Кроме того, choice будет фактически внешним ключом.

Может показаться, что столбец ответа может быть текстовым столбцом. Если разбить его на две или более колонки, такие как response-text и response-boolean, это выглядит грязно. Мы могли бы легко иметь сотни строк для одного пользователя.

Я также думал об организации таблицы следующим образом:

| user | survey | response (key-value store)                 |
|------+--------+--------------------------------------------|
|    1 |      1 | {"q1": "C", "q2": "1,2,4"                  |
|      |        | "q3": "It was the best of times..."        |
|      |        | "q4": "Other: I actually prefer bananas."} |
|    2 |      1 | {...}                                      |
|    3 |      1 | {...}                                      |

Как бы вы определили эту таблицу и почему?

Бонусные баллы, если вы объясните, в каком контексте ваше определение не сработает.

Если вы хотите представить мою ситуацию, вот несколько заметок о моем контексте:

  • Таблица ответов может обновляться несколько раз во время опроса, но никогда после этого. Эта конкретная таблица OLAP, или в основном для чтения. Единственные запросы, которые я буду выполнять, - экспортировать таблицу для статистического анализа в коммерческий программный пакет.
  • Ответы разных пользователей редко бывают идентичными (с высокого уровня)
  • Производительность не является проблемой в том смысле, что только 15 пользователей будут одновременно подключены к приложению во время сеанса администрирования опроса. Только один пользователь (я) будет просматривать данные после их сохранения.

Я подозреваю, что моя ситуация близка к примеру N1NF в Википедии .

Должна ли эта таблица быть полиморфной?

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

1 Ответ

4 голосов
/ 04 марта 2012

Я ведущий архитектор и разработчик http://360test.nl/, который использует опросы для измерения эффективности команды и т. Д.

Наша базовая структура таблиц ничем не отличается от вашей.

Полученные ответы хранятся в довольно простой таблице:

ID | Participation | Question | RawValue

Participation - это идентификатор участия одного пользователя в одном опросе. Question является идентификатором одного вопроса в опросе. RawValue хранит один единственный ответ. Формат зависит от типа вопроса. Это может быть одно число (для простого вопроса «выберите один») для более сложной конструкции json (например, для вопросов типа «распределить точки X по этим вариантам Y). Типы вопросов знают, как сериализовать / десериализовать данные к чему-то полезному.

Мы не используем наследование в наших таблицах. Это имело бы смысл, если бы нам были нужны онлайн-запросы, которые исследуют ответы («сколько пользователей выбрали ответ 3 в опросе xyz»), но нам это не нужно. Таким образом, мы можем использовать наш простой «универсальный» столбец RawValue. Когда мы показываем результаты опроса, все ответы загружаются, их значения десериализуются и оцениваются в соответствии с типом вопроса. Если нам нужны дополнительные специальные отчеты, мы генерируем их в автономном режиме и где-то храним результаты.

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

Я не говорю, что наш подход лучший, но он работает для нас.

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

Я написал программное обеспечение, используя архитектуру CQRS , и одну вещь, которую я узнал, это то, что денормализация может быть очень приятной вещью. Некоторые гуру CQRS предлагают одну таблицу на веб-страницу, которая содержит все (денормализованные) данные, необходимые для этой страницы. Хотя это не обязательно то, что вы должны делать постоянно, это показывает, что нормализация учебников не является обязательной во всех случаях.

...