SQL Studio / Разборы SQL / COUNT и NULL
Главное различие: COUNT(*) считает строки. COUNT(column) считает строки, где выбранный столбец не равен NULL. COUNT(DISTINCT column) считает уникальные ненулевые значения этого столбца. Это три разных показателя.
1. Один SQL-запрос — три разных числа
В учебной базе sessions находится девять посещений. Одно посещение анонимное (customer_id IS NULL), а известные клиенты приходили несколько раз. Выполним:
SELECT
COUNT(*) AS sessions_n,
COUNT(customer_id) AS known_sessions_n,
COUNT(DISTINCT customer_id) AS customers_n
FROM sessions;
| sessions_n | known_sessions_n | customers_n |
|---|---|---|
| 9 | 8 | 4 |
Девять — все события. Восемь — события с известным клиентом. Четыре — количество уникальных известных клиентов. Если вам нужен охват людей, сумма событий не подходит; если нужна нагрузка на систему, число людей может быть недостаточным.
2. Почему COUNT(*) при LEFT JOIN иногда даёт единицу вместо нуля
Создадим маленький пример: три клиента, у третьего нет посещений. LEFT JOIN всё равно вернёт строку клиента №3, заполнив правые поля NULL.
WITH c(customer_id) AS (
VALUES (1), (2), (3)
),
s(session_id, customer_id) AS (
VALUES (10, 1), (11, 1), (20, 2)
)
SELECT c.customer_id,
COUNT(*) AS joined_rows,
COUNT(s.session_id) AS sessions_n
FROM c
LEFT JOIN s ON s.customer_id = c.customer_id
GROUP BY c.customer_id
ORDER BY c.customer_id;
| customer_id | joined_rows | sessions_n |
|---|---|---|
| 1 | 2 | 2 |
| 2 | 1 | 1 |
| 3 | 1 | 0 |
При подсчёте посещений используйте COUNT(s.session_id), где session_id у существующего события не бывает пустым. Иначе клиент без посещений получит фиктивную единицу.
3. JOIN умножил строки — а сколько заказов было на самом деле?
В SQL Studio один заказ может иметь несколько попыток оплаты. Если соединить orders и payments, на один заказ получится несколько строк:
SELECT
COUNT(*) AS joined_rows,
COUNT(DISTINCT o.order_id) AS orders_n
FROM orders AS o
JOIN payments AS p ON p.order_id = o.order_id;
Результат — 8 строк после соединения и 6 уникальных заказов. COUNT(DISTINCT o.order_id) полезен для диагностики, но не исправляет неверную сумму денег: SUM(o.gross_kopecks) после такого JOIN всё ещё завысит итог. Для этого нужна другая структура запроса.
4. Уникальные клиенты по каналам
SELECT channel,
COUNT(*) AS sessions_n,
COUNT(DISTINCT customer_id) AS customers_n
FROM sessions
GROUP BY channel
ORDER BY sessions_n DESC, channel;
Этот запрос возвращает посещения и уникальных клиентов в каждом канале отдельно. Клиент, посещавший сайт через email и organic, будет учтён в обоих каналах. Поэтому сумму уникальных клиентов по каналам нельзя автоматически считать общим количеством уникальных клиентов сайта.
Попробуйте на практике
В тренажёре есть задания на уникальные города и количество сессий у каждого клиента. Начните с них, а затем проверьте свой вариант COUNT DISTINCT.
Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.