Задачи по SQL для junior-аналитика

Что ждут от junior

На собеседовании junior-аналитика проверяют:

  • SELECT, WHERE, ORDER BY — уверенно.
  • JOIN INNER/LEFT — без путаницы.
  • GROUP BY + агрегаты.
  • HAVING.
  • Базовые подзапросы.
  • Простые оконные (ROW_NUMBER).

Ниже 15 реальных задач junior-уровня.

Схема данных

Во всех задачах три таблицы:

  • users (user_id, name, city, registered_at)
  • products (product_id, name, category, price)
  • orders (order_id, user_id, product_id, amount, created_at)

1. Топ-10 самых активных пользователей

Найдите 10 пользователей с наибольшим количеством заказов.

SELECT u.name, COUNT(o.order_id) AS orders_count
FROM users u
JOIN orders o USING (user_id)
GROUP BY u.user_id, u.name
ORDER BY orders_count DESC
LIMIT 10;

2. Суммарные продажи по городам

SELECT u.city, SUM(o.amount) AS total_sales
FROM users u
JOIN orders o USING (user_id)
GROUP BY u.city
ORDER BY total_sales DESC;

3. Пользователи без заказов

SELECT u.*
FROM users u
LEFT JOIN orders o USING (user_id)
WHERE o.order_id IS NULL;

4. Средний чек в каждом городе

SELECT u.city, AVG(o.amount) AS avg_check
FROM users u
JOIN orders o USING (user_id)
GROUP BY u.city
ORDER BY avg_check DESC;

5. Товары, которые никто не купил

SELECT p.*
FROM products p
LEFT JOIN orders o USING (product_id)
WHERE o.order_id IS NULL;

Тренироваться на таких вопросах можно в Telegram-боте Карьерник — там 1500+ задач с реальных собесов с разборами.

6. Количество заказов по месяцам

SELECT DATE_TRUNC('month', created_at) AS month, COUNT(*) AS orders
FROM orders
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month;

7. Категории с выручкой больше 1М

SELECT p.category, SUM(o.amount) AS revenue
FROM products p
JOIN orders o USING (product_id)
GROUP BY p.category
HAVING SUM(o.amount) > 1000000;

8. Первый заказ каждого пользователя

SELECT user_id, MIN(created_at) AS first_order
FROM orders
GROUP BY user_id;

9. Второй способ — через ROW_NUMBER

WITH ranked AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at) AS rn
    FROM orders
)
SELECT * FROM ranked WHERE rn = 1;

Чем отличается от предыдущей: возвращает всю строку заказа, не только дату.

10. Пользователи с заказами в марте И апреле 2026

SELECT user_id
FROM orders
WHERE created_at >= '2026-03-01' AND created_at < '2026-05-01'
GROUP BY user_id
HAVING COUNT(DISTINCT DATE_TRUNC('month', created_at)) = 2;

11. Топ-1 товар в каждой категории

WITH ranked AS (
    SELECT p.category, p.product_id, SUM(o.amount) AS revenue,
        ROW_NUMBER() OVER (PARTITION BY p.category ORDER BY SUM(o.amount) DESC) AS rn
    FROM products p
    JOIN orders o USING (product_id)
    GROUP BY p.category, p.product_id
)
SELECT * FROM ranked WHERE rn = 1;

12. Процент заказов со средним чеком выше 1000

SELECT
    COUNT(CASE WHEN amount > 1000 THEN 1 END) * 100.0 / COUNT(*) AS pct
FROM orders;

Или через FILTER (PostgreSQL):

SELECT COUNT(*) FILTER (WHERE amount > 1000) * 100.0 / COUNT(*) AS pct
FROM orders;

К слову, набить руку на таких кейсах удобно через тренажёр в Telegram — разбирайте по 10 вопросов в день, через 2 недели тема становится рефлексом.

13. Пользователи, сделавшие ≥5 заказов

SELECT u.name, COUNT(o.order_id) AS cnt
FROM users u
JOIN orders o USING (user_id)
GROUP BY u.user_id, u.name
HAVING COUNT(o.order_id) >= 5
ORDER BY cnt DESC;

14. Ежедневная выручка с нарастающим итогом

SELECT
    created_at::DATE AS day,
    SUM(amount) AS daily,
    SUM(SUM(amount)) OVER (ORDER BY created_at::DATE) AS cumulative
FROM orders
GROUP BY created_at::DATE
ORDER BY day;

15. Клиенты, которые купили все три товара A, B, C

SELECT user_id
FROM orders
WHERE product_id IN ('A', 'B', 'C')
GROUP BY user_id
HAVING COUNT(DISTINCT product_id) = 3;

Что junior делает не так

  • Забывает GROUP BY. Если в SELECT есть агрегат и поле — поле должно быть в GROUP BY.
  • WHERE на агрегате. Агрегат в WHERE — ошибка, используйте HAVING.
  • Integer division. 3/2 = 1, не 1.5. Используйте 3.0/2 или CAST(3 AS NUMERIC)/2.
  • NOT IN с NULL. Если подзапрос содержит NULL, NOT IN вернёт 0 строк.
  • LEFT JOIN + WHERE на правой таблице. Превращает LEFT в INNER.

Как тренироваться

Решайте 3-5 задач в день в течение 2-3 недель. Тренажёр Карьерник содержит 200+ задач разного уровня с разборами.

Совет: на собесе, прежде чем писать код, проговорите логику вслух. «Мне нужно сгруппировать по городу, посчитать сумму, отсортировать по убыванию» — интервьюер оценит даже до того, как вы напишите первую строку.

Читайте также

FAQ

Сколько SQL задач нужно решить до собеса?

Junior — 100-150 задач разного уровня. За 3-4 недели это реально.

Что делать, если не знаю синтаксис?

На junior-собесе интервьюер часто подсказывает. Главное — чёткая логика и чистый псевдокод. Синтаксис доучите потом.

Нужно ли оконные функции для junior?

Базовые (ROW_NUMBER) — да. LAG, LEAD, SUM OVER — плюс, но не обязательно.

Что такое «junior-уровень» SQL?

SELECT, WHERE, ORDER BY, JOIN (INNER, LEFT), GROUP BY + агрегаты, HAVING, подзапросы, базовые оконные. Оконные функции с ROWS BETWEEN, рекурсивные CTE — middle.