💬 Какой результат SQL отправить бизнесу?
Ребят, одна из частых ошибок в SQL-задачах состоит в том, что мы сразу открываем редактор, механически считаем показатель и отправляем заказчику готовую цифру. Я сам не раз на этом обжигался: запрос написан правильно, результат выглядит убедительно, а потом выясняется, что под одной и той же формулировкой мы понимали разные вещи.
Покажу на небольшом примере.
Менеджер присылает таблицу с заказами за сентябрь и просит посчитать, сколько клиентов было за месяц. Вроде бы обычная задача на COUNT, которую можно закрыть одним запросом.
order_id | customer_id | order_date ---------+-------------+----------- 1001 | 1 | 2026-09-02 1002 | 1 | 2026-09-11 1003 | 2 | 2026-09-04 1004 | 3 | 2026-09-06 1005 | 3 | 2026-09-18 1006 | 4 | 2026-09-22
На первый взгляд задача простая, но по этим данным можно вернуть как минимум три правдоподобных ответа: 6, 4 или 2. Какой из них вы бы отправили менеджеру?
Попробуйте сначала выбрать свой вариант, а затем посмотреть разбор ниже.
Теперь разбираемся
Если выполнить COUNT(*), получится 6, но это количество заказов, поскольку одна строка в таблице соответствует одному заказу. Запрос выполнился правильно, только на вопрос о клиентах он не ответил.
COUNT(DISTINCT customer_id) вернёт 4. Это количество уникальных клиентов, которые сделали хотя бы один заказ в сентябре, и именно такой смысл чаще всего подразумевают под числом покупателей за период.
Если сгруппировать данные по customer_id и оставить клиентов с двумя и более заказами, получится 2. Но называть их повторными клиентами пока рано: мы видим только сентябрь и не знаем, покупали ли клиенты 2 и 4 раньше. Для одного бизнеса повторная покупка означает второй заказ внутри месяца, а для другого любой заказ после первой покупки за всё время.
Все три числа можно получить корректным SQL-запросом:
with customer_orders as ( select customer_id, count() as orders_cnt from orders where order_date >= '2026-09-01' and order_date < '2026-10-01' group by customer_id ) select sum(orders_cnt) as orders_cnt, count() as buyers_cnt, sum( case when orders_cnt >= 2 then 1 else 0 end ) as clients_with_2plus_orders from customer_orders;
Обратите внимание на границу периода: условие order_date < '2026-10-01' безопаснее, чем попытка перечислить все возможные значения последнего дня сентября, особенно если в поле хранится не только дата, но и время.
Результат будет таким: orders_cnt | buyers_cnt | clients_with_2plus_orders -----------+------------+-------------------------- 6 | 4 | 2
При этом правильного ответа на фразу «сколько клиентов было за месяц» всё ещё нет, пока мы не договорились о смысле метрики. Вместо того чтобы угадывать, я бы вернулся к менеджеру с конкретным вопросом:
Уточню смысл: считаем всех уникальных покупателей с заказом в сентябре, клиентов с двумя и более заказами внутри месяца или тех, кто в сентябре вернулся после своей первой покупки за всю историю?
В работе к этому добавятся статусы заказов, возвраты, границы периода и часовой пояс. Самая опасная ошибка здесь появляется ещё до написания SQL, когда расплывчатую формулировку превращают в точную цифру, а потом эта цифра попадает в отчёт и начинает выглядеть как факт.
Поэтому я бы не отправлял ни 6, ни 4, ни 2, пока не зафиксирован смысл метрики. Хороший запрос начинается с ответа на два вопроса: что означает одна строка исходных данных и какой бизнес-смысл должна иметь итоговая цифра.
❔ Если хотите проверить, где у вас заканчивается уверенный SQL, на сайте есть страница с небольшой диагностикой.