SQL Studio / Разборы SQL / Как посчитать средний чек в SQL и не завысить его
Формула: средний чек по доставленным заказам = сумма стоимости доставленных заказов / количество доставленных заказов. Числитель и знаменатель должны описывать одну и ту же аудиторию и период. В примерах ниже чек — валовая стоимость заказа, а не чистые поступления после возвратов.
1. Правильный расчёт по таблице заказов
В учебной базе одна строка orders соответствует одному заказу. Отбираем только status = 'delivered' и работаем с суммой в копейках.
SELECT COUNT(*) AS delivered_orders,
SUM(gross_kopecks) / 100.0 AS gross_rub,
ROUND(SUM(gross_kopecks) * 1.0 / NULLIF(COUNT(*), 0) / 100, 2) AS aov_rub
FROM orders
WHERE status = 'delivered';
| delivered_orders | gross_rub | aov_rub |
|---|---|---|
| 4 | 1250 | 312.50 |
В результате четыре заказа и сумма 1 250 ₽. Средний чек — 312,50 ₽. Деление на 100 переводит копейки в рубли, а NULLIF защищает от деления на ноль.
2. Ошибка: после JOIN сумма заказов выросла
В одном заказе может быть несколько товарных позиций. Если сначала соединить заказы с позициями, стоимость заказа повторится для каждой позиции.
SELECT COUNT(*) AS joined_rows,
COUNT(DISTINCT o.order_id) AS delivered_orders,
SUM(o.gross_kopecks) / 100.0 AS wrong_gross_rub
FROM orders AS o
JOIN order_items AS i ON i.order_id = o.order_id
WHERE o.status = 'delivered';
Получится 7 строк на те же 4 заказа, а сумма вырастет с 1 250 до 2 200 ₽. Если разделить эту сумму на четыре заказа, получится ошибочный средний чек 550 ₽. COUNT(DISTINCT) не исправляет уже размноженную денежную сумму.
3. Более короткий правильный вариант через AVG
WITH delivered AS (
SELECT order_id, gross_kopecks
FROM orders
WHERE status = 'delivered'
)
SELECT ROUND(AVG(gross_kopecks) / 100.0, 2) AS aov_rub
FROM delivered;
Вернётся 312,50 ₽. AVG подходит, потому что каждая строка CTE — отдельный доставленный заказ. Не делайте AVG после соединения с позициями: тогда вес заказа будет зависеть от количества его позиций.
4. Что делать, если за период не было заказов
SELECT ROUND(
SUM(gross_kopecks) / 100.0 / NULLIF(COUNT(*), 0), 2
) AS aov_rub
FROM orders
WHERE status = 'delivered'
AND created_at >= '2026-10-01'
AND created_at < '2026-11-01';
Для октября в нашем наборе нет заказов, поэтому результат — NULL. Это информативнее, чем автоматически поставить 0 ₽: покупок не было, а не «средний покупатель потратил ноль». В пользовательском отчёте можно показать «Нет заказов».
Практика в тренажёре
Посчитайте валовую стоимость доставленных заказов, затем намеренно присоедините позиции и проверьте, как изменился итог.
Справка о SUM, AVG и COUNT в SQLite.
Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.