Как посчитать средний чек в SQL и не завысить его

Четыре доставленных заказа на 1 250 ₽ дают средний чек 312,50 ₽. Но после JOIN с позициями получается другой итог. Найдём причину и исправим SQL.

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_ordersgross_rubaov_rub
41250312.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 ₽: покупок не было, а не «средний покупатель потратил ноль». В пользовательском отчёте можно показать «Нет заказов».

Практика в тренажёре

Посчитайте валовую стоимость доставленных заказов, затем намеренно присоедините позиции и проверьте, как изменился итог.

По теме: JOIN и завышение суммы · оплаты и возвраты · COUNT DISTINCT.

Справка о SUM, AVG и COUNT в SQLite.


Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.