Оптимизация соединения с БД и повторение столбцов
Это больше похоже на предпочтения, но мне было интересно, что люди думают, что будет оптимальным вариантом для выполнения. У меня есть вопрос, ответ и точка (потому что мне нужно отследить, какой пользователь высказал точку)
Настольный дамп
Question:
id
title
Answer:
id
question_id
user_id
response
Point_Answer:
id
answer_id
user_id
points
Таким образом, в этом макете для получения Верхнего ответа потребуется сложная последовательность соединений.
SELECT t2.id, t2.user_id, t2.response, MAX(points)
FROM Question as t1,
(SELECT qa.*, SUM(pa.points) as points
FROM answer as qa, Point_Answer as pa
WHERE qa.id = pa.answer_id
GROUP BY qa.id) as t2
WHERE t1.id = %s AND t1.id = t2.question_id
Где, если я изменил это так:
Question:
id
title
Answer:
id
question_id
user_id
response
points
Point_Answer:
id
answer_id
user_id
points
Запрос будет менее обременительным
SELECT A.id, A.user_id, A.response, MAX(points)
FROM Question as Q, Answer as A
WHERE Q.id = %s AND Q.id = A.question_id
GROUP BY A.id
Также будет означать, что я должен убедиться, что при добавлении Point_Answer будут добавлены точки ответа. Так что в основном дополнительное обновление. По сути, это "Целостность против избыточности" и небольшая оптимизация, каким будет лучший путь?
3 ответа
Это будет зависеть от того, насколько медленным является не сложность объединения. Было бы крайне плохой идеей делать это исключительно потому, что вы не хотите писать (один раз) более сложный запрос. Производительность - единственная реальная причина сделать что-то подобное.
Если первое является недопустимо медленным, то таблица или поле, суммирующее точки, может быть приемлемой денормализацией, если и ТОЛЬКО если вы обновляете поле с помощью триггера, а не из приложения (единственный способ обеспечить точность денормализованного числа). Вам нужно будет протестировать решение, включая дополнительное время обновления, чтобы определить, действительно ли вы сэкономили время обработки. Это может зависеть от того, как часто цифры меняются. Например, если вы добавляете секунду ко времени обновления и сохраняете десять секунд для выбора, но при этом вы получаете 10000 обновлений для каждого элемента, это не очень хорошая оптимизация. Однако, если вы создадите отчет, который будет длиться от часа до миллисекунд и добавите только миллисекунду к вставке или обновлению, это может быть приемлемым.
Невозможно ответить на этот вопрос без фактического кодирования и тестирования обоих решений с рабочей нагрузкой на уровне производства и данными.
Это зависит от многих факторов, большинство из которых зависит от вашей настройки.
Два наиболее важных фактора:
- Как часто вы выполняете запрос. Имейте в виду, что второе решение не только использует больше дискового пространства (что теоретически может снизить производительность), но также требует, чтобы вы заботились о денормализованной структуре при добавлении записей. Хотя это может быть автоматизировано с помощью триггера (в зависимости от СУБД), это все равно приводит к снижению производительности.
- СУБД, которую вы используете. Ваш первый запрос может быть уродливым (я видел еще хуже), но вы уверены, что он медленный? Единственный способ получить окончательный ответ на этот вопрос - запустить запрос и проверить с помощью EXPLAIN [запрос], какой план запроса используется вашей СУБД.
В общем, я бы придерживался первого решения. Отсутствие нормализованной схемы отношений иногда хорошо, но вам следует денормализовать свою структуру, если вы уверены, это даст вам повышение производительности и если вы определили узкое место в вашем приложении в производственной среде.
Если запрос выполняется достаточно хорошо, я бы оставил его как есть. Уродливый, хорошо работающий запрос превосходит избыточность в моей книге.
С опцией избыточности вы должны убедиться, что вы инкапсулировали свои операторы обновления в транзакцию, чтобы убедиться, что все обновляется; в противном случае вы рискуете не синхронизировать свои данные.
Я работал с некоторыми унаследованными приложениями, которые пошли по пути избыточности без транзакций, и когда одна таблица не обновляется по какой-либо причине, она становится грязной.