#mysql #sql
#mysql #sql
Вопрос:
У меня есть этот SQL в моем коде, и повторение вложенных запросов заставляет меня беспокоиться о том, что это написано не так эффективно, как могло бы быть.
В моей базе данных есть таблица для features
, responses
, и participants
. Участники дают оценку (одну из D, P, B, I, R, Q) для каждой функции.
SELECT f.id, f.name,
(SELECT COUNT(r.id) FROM responses r LEFT JOIN participants p ON r.id_participant = p.id WHERE p.is_ignored IS NULL AND r.is_deleted IS NULL AND r.id_feature = f.id AND p.category LIKE :p_category) AS nr_r,
(SELECT SUM(r.is_D) FROM responses r LEFT JOIN participants p ON r.id_participant = p.id WHERE p.is_ignored IS NULL AND r.is_deleted IS NULL AND r.id_feature = f.id AND p.category LIKE :p_category) AS is_D,
(SELECT SUM(r.is_P) FROM responses r LEFT JOIN participants p ON r.id_participant = p.id WHERE p.is_ignored IS NULL AND r.is_deleted IS NULL AND r.id_feature = f.id AND p.category LIKE :p_category) AS is_P,
(SELECT SUM(r.is_B) FROM responses r LEFT JOIN participants p ON r.id_participant = p.id WHERE p.is_ignored IS NULL AND r.is_deleted IS NULL AND r.id_feature = f.id AND p.category LIKE :p_category) AS is_B,
(SELECT SUM(r.is_I) FROM responses r LEFT JOIN participants p ON r.id_participant = p.id WHERE p.is_ignored IS NULL AND r.is_deleted IS NULL AND r.id_feature = f.id AND p.category LIKE :p_category) AS is_I,
(SELECT SUM(r.is_R) FROM responses r LEFT JOIN participants p ON r.id_participant = p.id WHERE p.is_ignored IS NULL AND r.is_deleted IS NULL AND r.id_feature = f.id AND p.category LIKE :p_category) AS is_R,
(SELECT SUM(r.is_Q) FROM responses r LEFT JOIN participants p ON r.id_participant = p.id WHERE p.is_ignored IS NULL AND r.is_deleted IS NULL AND r.id_feature = f.id AND p.category LIKE :p_category) AS is_Q,
(SELECT (is_D is_P)/(is_D is_P is_B is_I)) as yay,
(SELECT (is_P is_B)/(is_D is_P is_B is_I)) as boo
FROM features f
WHERE f.is_deleted IS NULL AND f.id_survey=:id_project
ORDER BY f.id ASC;
Результатом этого запроса является таблица итогов, выглядящая примерно так
577 App registration 989 36 21 38 201 42 6 0.1926 0.1993
578 Login PIN 989 279 118 137 394 41 20 0.4278 0.2748
579 Manage all services 989 287 207 127 331 23 14 0.5189 0.3508
580 Rewards 989 344 157 64 386 19 19 0.5268 0.2324
581 Offers 989 226 93 37 542 72 19 0.3552 0.1448
В настоящее время выполнение запроса для набора данных с 989 участниками, 14 функциями (и, следовательно, 13 846 индивидуальных оценок) занимает 4,4387 секунды. Уверен, что это воняет.
Есть ли более эффективный способ написания этого запроса?
Является ли это чем-то, с чем GROUP BY была бы полезна?
Ответ №1:
Если я правильно понимаю ваш запрос, у вас одинаковые предикаты в каждом подзапросе, а затем вы можете легко заменить все подвыборки на объединение и выполнить group by . Вычисления для агрегатов (yay и boo) вычисляются на внешнем уровне:
SELECT id, name, nr_r, is_D, ...
, (is_D is_P)/(is_D is_P is_B is_I) yay
, (is_P is_B)/(is_D is_P is_B is_I) boo
FROM (
SELECT f.id, f.name
, count(r.id) AS nr_r
, sum(r.is_D) as is_D
, ...
FROM features f
LEFT JOIN responses r
ON r.id_feature = f.id
AND r.is_deleted IS NULL
LEFT JOIN participants p
ON r.id_participant = p.id
AND p.is_ignored IS NULL
AND p.category LIKE :p_category
WHERE f.is_deleted IS NULL
AND f.id_survey=:id_project
GROUP BY f.id, f.name
) AS T
ORDER BY ...
Комментарии:
1. Если вы хотите a
LEFT JOIN
, тогда вам следует переместить ограничения для таблиц, соединенных слева, изWHERE
предложения в условие соединения (т. Е.r.id_feature = f.id
), Потому что иначе вы получаете неявноINNER JOIN
s2. Вы правы, я недостаточно внимательно изучил запрос. Он уже указан в предложении join, поэтому его можно удалить из предложения where . Я обновлю ответ
3. Это довольно близко, но я получаю неправильные ответы для столбцов yay и boo — куча 1.0, 0.0 и нулей. Я посмотрю, смогу ли я заставить его работать.
4. Насколько я могу судить, он выполняет вычисления для yay и boo в отдельных строках ответов до того, как вступит в силу GROUP BY (только 1 из is_D / is_P / etc имеет значение 1, все остальные будут равны 0 или все будут равны НУЛЮ)
5. Да, это так, что вы можете сделать, это вложить эти вычисления, я добавлю это к моему примеру.