SQL Studio / Разборы SQL / Почему JOIN увеличивает выручку
Откуда берутся лишние строки
Для воспроизводимого примера создадим две маленькие таблицы прямо внутри SQL-запроса: sample_orders содержит заказы на 1 000 ₽ и 500 ₽, а sample_items — две позиции первого заказа и одну второго. Соединение размножит сумму первого заказа.
WITH sample_orders(order_id, total_rub) AS (
VALUES (1, 1000), (2, 500)
),
sample_items(order_id) AS (
VALUES (1), (1), (2)
)
SELECT SUM(o.total_rub) AS wrong_rub
FROM sample_orders AS o
JOIN sample_items AS i USING (order_id);
Результат такого запроса — 2 500 ₽ вместо 1 500 ₽. Данные не обязательно неверны: соединение изменило уровень детализации с «одна строка — заказ» на «одна строка — позиция заказа».
Как посчитать правильно
Для правильной суммы этих двух заказов достаточно одной строки на заказ. Не нужно соединять их с позициями:
WITH sample_orders(order_id, total_rub) AS (
VALUES (1, 1000), (2, 500)
)
SELECT SUM(total_rub) AS correct_rub
FROM sample_orders;
На настоящих учебных таблицах SQL Studio суммы хранятся в копейках в столбце orders.gross_kopecks. Если вместе с суммой заказов нужно получить количество позиций, сначала сверните позиции до одной строки на заказ, затем соединяйте:
WITH items_per_order AS (
SELECT order_id, COUNT(*) AS items_n
FROM order_items
GROUP BY order_id
)
SELECT
SUM(o.gross_kopecks) / 100.0 AS gross_rub,
SUM(COALESCE(i.items_n, 0)) AS positions_n
FROM orders AS o
LEFT JOIN items_per_order AS i
ON i.order_id = o.order_id
WHERE o.status = 'delivered';Полученное gross_rub — сумма стоимости доставленных заказов, а не подтверждённая сумма оплат за вычетом возвратов. Для финансовой выручки проверяйте таблицы payments и refunds отдельно.
Как быстро проверить размножение строк на реальных данных
Не начинайте исправление с SELECT DISTINCT или SUM(DISTINCT amount). Второй вариант вообще потеряет один из заказов, если у разных заказов совпадает сумма. Сначала найдите ключи, для которых соединение вернуло больше одной строки.
SELECT o.order_id, COUNT(*) AS rows_after_join
FROM orders AS o
JOIN order_items AS i ON i.order_id = o.order_id
GROUP BY o.order_id
HAVING COUNT(*) > 1
ORDER BY o.order_id;
На маленькой базе SQL Studio запрос покажет заказы 201, 203 и 205: у каждого по две товарные позиции. Это не ошибка таблицы заказов — это ожидаемая детализация таблицы позиций.
Контроль: сравните COUNT(*) со значением COUNT(DISTINCT o.order_id). Если они отличаются, перед суммированием стоимости заказа верните детализацию к одной строке на заказ.
Попрактикуйтесь
В бесплатном тренажёре откройте задачу на соединение заказов с клиентами. Затем попробуйте самостоятельно посчитать заказы по городам и проверить количество строк.
Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.