SQL Studio / Разборы SQL / Пять SQL-задач для собеседования аналитика
Перед вами пять коротких задач на одной учебной базе customers, orders, payments, refunds. Попробуйте решить каждую самостоятельно в бесплатной песочнице, а затем раскрывайте решение. Установка MySQL не нужна: здесь используется SQLite; все приведённые примеры проверяются на учебной базе сайта.
Оцените себя: по одному баллу за запрос, корректный результат и объяснение ключевого ограничения. Максимум 15 баллов. Это самостоятельный ориентир, не официальная шкала собеседований.
1. Клиенты без заказов
Найдите идентификаторы и имена клиентов, у которых нет ни одного заказа. Нужно сохранить корректность при NULL в других полях.
На что обратить внимание: Отсутствие связанных строк удобно проверить через NOT EXISTS.
Показать решение и разбор
SELECT c.customer_id, c.name
FROM customers AS c
WHERE NOT EXISTS (
SELECT 1
FROM orders AS o
WHERE o.customer_id = c.customer_id
)
ORDER BY c.customer_id;
Ответ: Данил (customer_id=4). В отличие от NOT IN, вариант с NOT EXISTS не попадает в ловушку NULL при построении множества идентификаторов.
2. Доставки у каждого клиента, включая нули
Выведите customer_id и число доставленных заказов у каждого зарегистрированного клиента. Клиенты без доставок должны получить 0.
На что обратить внимание: Фильтр статуса лучше включить в условие JOIN, а COUNT(*) при LEFT JOIN здесь неверен.
Показать решение и разбор
SELECT c.customer_id,
COUNT(o.order_id) AS deliveries_n
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.customer_id
AND o.status = 'delivered'
GROUP BY c.customer_id
ORDER BY c.customer_id;
Результат: (1,2), (2,1), (3,1), (4,0). Используем COUNT(o.order_id) — счётчик непустых совпавших заказов.
3. Поступления за вычетом возвратов
Посчитайте общий итог captured-платежей за вычетом суммы возвратов, в копейках. Не соединяйте payments и refunds напрямую.
На что обратить внимание: Две отдельные агрегированные суммы безопаснее соединения двух отношений «один ко многим».
Показать решение и разбор
SELECT
(SELECT COALESCE(SUM(amount_kopecks), 0)
FROM payments WHERE status = 'captured')
-
(SELECT COALESCE(SUM(amount_kopecks), 0)
FROM refunds) AS net_kopecks;
В учебной базе captured-платежи составляют 125000 копеек, возвраты — 9000, итог — 116000. Это учебная модель денежного итога: в реальном учёте нужны правила проведения оплат и возвратов.
4. Последний заказ каждого клиента
Получите по одному последнему заказу на клиента по дате создания. При одинаковом времени выбирайте больший order_id.
На что обратить внимание: ROW_NUMBER с PARTITION BY customer_id и дополнительным ключом сортировки.
Показать решение и разбор
WITH ranked AS (
SELECT customer_id, order_id,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY created_at DESC, order_id DESC
) AS rn
FROM orders
)
SELECT customer_id, order_id
FROM ranked
WHERE rn = 1
ORDER BY customer_id;
В маленькой базе: (1,203), (2,206), (3,205). У Данила заказа нет, поэтому в этом варианте результата он не появляется.
5. Включить весь последний день отчёта
Получите идентификаторы заказов с 1 по 5 сентября 2026 года включительно. Столбец created_at содержит время.
На что обратить внимание: Полуоткрытый диапазон исключает первую секунду следующего дня, но включает все моменты последнего.
Показать решение и разбор
SELECT order_id
FROM orders
WHERE created_at >= '2026-09-01'
AND created_at < '2026-09-06'
ORDER BY order_id;
Ответ: 201, 202, 203, 204, 205. Использование BETWEEN до '2026-09-05' исключило бы запись с временем 15:15.
После самостоятельной проверки
Если ошиблись в задачах 1–2, повторите LEFT JOIN и NULL. Если в задаче 3 — двойной подсчёт после JOIN. Если в задаче 4 — оконные функции для последней записи. Если в задаче 5 — границы дат.
Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.