#mysql #sql
#mysql #sql
Вопрос:
Для этого примера я получил 3 простые таблицы (страница, подразделы и подписчики):
Для каждой страницы мне нужно знать, сколько у нее дочерних элементов и подписчиков. Мой результат должен выглядеть следующим образом:
Я попытался использовать функцию COUNT в сочетании с GROUP BY следующим образом:
SELECT p.ID, COUNT(s.UID) AS SubCount, COUNT(f.UID) AS FollowCount
FROM page p, subs s, followers f
WHERE p.ID = s.ID AND p.ID = f.ID AND s.ID = f.ID
GROUP BY p.ID
Очевидно, что это утверждение возвращает неверный результат.
Моей другой попыткой было использование двух разных операторов SELECT, а затем объединение двух вложенных результатов в одну таблицу.
SELECT p.ID, COUNT(s.UID) AS SubCount FROM page p, subs s WHERE p.ID = s.ID GROUP BY p.ID
и
SELECT p.ID, COUNT(f.UID) AS FollowCount FROM page p, follow f WHERE p.ID = f.ID GROUP BY p.ID
Я чувствую, что должен быть более простой / короткий способ сделать это, но я слишком неопытен, чтобы найти его.
Ответ №1:
Никогда не используйте запятые в FROM
предложении. Всегда используйте правильный, явный, стандартный JOIN
синтаксис.
Далее узнайте, что COUNT()
делает. Подсчитывает количество ненулевых значений. Итак, ваши выражения будут возвращать одно и то же значение — потому что f.UID
и s.UID
никогда не NULL
(из-за JOIN
условий).
Проблема в том, что разные измерения умножают суммы. Простое решение заключается в использовании COUNT(DISTINCT)
:
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p JOIN
subs s
ON p.ID = s.ID JOIN
followers f
ON s.ID = f.ID
GROUP BY p.ID;
Внутренние соединения эквивалентны исходному запросу. Вероятно, вам нужны left join
s, чтобы вы могли получать нулевые значения:
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p LEFT JOIN
subs s
ON p.ID = s.ID LEFT JOIN
followers f
ON p.ID = f.ID
GROUP BY p.ID;
Комментарии:
1. Ваше решение работает до тех пор, пока на странице нет 0 подписчиков или вспомогательных страниц. Я удалил последнюю запись моей вспомогательной таблицы, чтобы на странице с идентификатором 2 было 0 вспомогательных файлов и 4 подписчика. Теперь идентификатор 2 вообще не отображается. Я заменил все соединения левыми соединениями. Теперь идентификатор 2 отображается со значениями 0 Subs и 0 Followers (хотя у него 4 Followers). Вы знаете, что я там сделал не так?
2. @RolfWolf . . . Второе соединение должно быть с
p.id
, а неs.id
.3. Накладные расходы на декартово произведение, произведенное между
subs
иfollowers
таким способом, могут быть значительными, если числа высоки. Я думаю, что в этом случае лучше использовать решение с коррелированными подзапросами…4. @LukasEder . . . Или путем агрегирования перед присоединением. Мой ответ был направлен на то, как OP первоначально сформулировал запрос.
Ответ №2:
В этом случае должен сработать скалярный подзапрос.
SELECT p.id,
(SELECT Count(s_uid)
FROM subs s1
WHERE s1.s_id = p.id) AS cnt_subs,
(SELECT Count(f_uid)
FROM followers f1
WHERE f1.f_id = p.id) AS cnt_fol
FROM page p
GROUP BY p.id;