SQL — главный инструмент аналитика данных. Не Python, не Excel — именно SQL. По данным опросов, 80% аналитиков используют SQL ежедневно. В этой статье — что конкретно нужно знать, с реальными примерами задач.
Уровень 1: базовый SELECT (без него никуда)
Начнём с основ, которые нужны всегда.
-- Выборка с фильтрацией и сортировкой
SELECT
user_id,
order_date,
amount,
status
FROM orders
WHERE status = 'paid'
AND order_date >= '2026-01-01'
ORDER BY amount DESC
LIMIT 100;
Что нужно знать твёрдо:
WHEREс несколькими условиями (AND,OR,NOT,IN,BETWEEN,LIKE)ORDER BY+LIMIT/OFFSETDISTINCTдля уникальных значений- Работа с
NULL:IS NULL,COALESCE
Уровень 2: агрегация — основа аналитики
90% аналитических задач сводятся к «посчитать что-то по группам».
-- Классическая задача: метрики по когортам
SELECT
DATE_TRUNC('month', created_at) AS cohort_month,
COUNT(DISTINCT user_id) AS new_users,
SUM(amount) AS revenue,
AVG(amount) AS avg_order,
COUNT(*) AS orders_count
FROM orders
WHERE status = 'paid'
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY cohort_month;
Типичные аналитические задачи:
-- Конверсия по источникам трафика
SELECT
source,
COUNT(DISTINCT session_id) AS sessions,
COUNT(DISTINCT CASE WHEN converted THEN user_id END) AS conversions,
ROUND(
100.0 * COUNT(DISTINCT CASE WHEN converted THEN user_id END)
/ NULLIF(COUNT(DISTINCT session_id), 0),
2
) AS conversion_rate_pct
FROM sessions
GROUP BY source
ORDER BY sessions DESC;
Уровень 3: JOIN — соединять данные из нескольких таблиц
В реальной аналитике данные всегда разбиты по таблицам.
-- LTV пользователя: выручка за всё время
SELECT
u.id,
u.email,
u.created_at AS registration_date,
COUNT(o.id) AS total_orders,
COALESCE(SUM(o.amount), 0) AS ltv,
MAX(o.created_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON o.user_id = u.id AND o.status = 'paid'
GROUP BY u.id, u.email, u.created_at
ORDER BY ltv DESC;
LEFT JOINвместоINNER JOIN— чтобы пользователи без заказов тоже попали в отчёт с LTV = 0.
Уровень 4: оконные функции — продвинутая аналитика
Без оконных функций невозможно делать retention, когортный анализ, ранжирование.
-- Retention: вернулся ли пользователь на следующей неделе
WITH weekly_activity AS (
SELECT
user_id,
DATE_TRUNC('week', event_date) AS week
FROM events
GROUP BY user_id, DATE_TRUNC('week', event_date)
),
retention AS (
SELECT
w1.week AS cohort_week,
COUNT(DISTINCT w1.user_id) AS users,
COUNT(DISTINCT w2.user_id) AS retained
FROM weekly_activity w1
LEFT JOIN weekly_activity w2
ON w1.user_id = w2.user_id
AND w2.week = w1.week + INTERVAL '1 week'
GROUP BY w1.week
)
SELECT
cohort_week,
users,
retained,
ROUND(100.0 * retained / NULLIF(users, 0), 1) AS retention_pct
FROM retention
ORDER BY cohort_week;
-- Топ-3 продукта по выручке в каждой категории
SELECT category, product_name, revenue
FROM (
SELECT
category,
product_name,
SUM(amount) AS revenue,
ROW_NUMBER() OVER (
PARTITION BY category
ORDER BY SUM(amount) DESC
) AS rn
FROM order_items oi
JOIN products p ON p.id = oi.product_id
GROUP BY category, product_name
) t
WHERE rn <= 3;
Уровень 5: CTE для сложных отчётов
Сложный отчёт — это несколько шагов. CTE делает это читаемым.
-- Воронка продаж
WITH
funnel_steps AS (
SELECT
user_id,
MAX(CASE WHEN event = 'page_view' THEN 1 ELSE 0 END) AS visited,
MAX(CASE WHEN event = 'add_to_cart' THEN 1 ELSE 0 END) AS added,
MAX(CASE WHEN event = 'checkout_start' THEN 1 ELSE 0 END) AS checkout,
MAX(CASE WHEN event = 'purchase' THEN 1 ELSE 0 END) AS purchased
FROM events
WHERE event_date >= CURRENT_DATE - 30
GROUP BY user_id
)
SELECT
SUM(visited) AS step1_visited,
SUM(added) AS step2_added_to_cart,
SUM(checkout) AS step3_checkout,
SUM(purchased) AS step4_purchased,
ROUND(100.0 * SUM(added) / NULLIF(SUM(visited), 0), 1) AS s1_to_s2,
ROUND(100.0 * SUM(checkout) / NULLIF(SUM(added), 0), 1) AS s2_to_s3,
ROUND(100.0 * SUM(purchased) / NULLIF(SUM(checkout), 0), 1) AS s3_to_s4
FROM funnel_steps;
Реальные задачи с собеседований
На собеседованиях аналитиков чаще всего просят:
1. Найти пользователей, не вернувшихся после первого заказа
SELECT user_id
FROM orders
GROUP BY user_id
HAVING COUNT(*) = 1;
2. Скользящее среднее за 7 дней
SELECT
order_date,
revenue,
AVG(revenue) OVER (
ORDER BY order_date
ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
) AS rolling_7d_avg
FROM daily_revenue;
3. Доля каждого продукта в выручке категории
SELECT
category,
product_name,
revenue,
ROUND(
100.0 * revenue / SUM(revenue) OVER (PARTITION BY category),
1
) AS pct_of_category
FROM product_revenue
ORDER BY category, revenue DESC;
Чек-лист аналитика
| Тема | Нужно уметь |
|---|---|
| SELECT / WHERE / ORDER | Базовая фильтрация и сортировка |
| GROUP BY + агрегаты | COUNT, SUM, AVG, MIN/MAX, HAVING |
| JOIN | INNER, LEFT; поиск «сирот» через IS NULL |
| Оконные функции | ROW_NUMBER, RANK, LAG/LEAD, SUM OVER |
| CTE | WITH, вложенные CTE, рекурсия |
| Даты | DATE_TRUNC, EXTRACT, INTERVAL, AGE |
| NULL | COALESCE, NULLIF, IS NULL |
| Подзапросы | EXISTS, IN, коррелированные |
Хочешь прокачать SQL для аналитики? В нашем тренажёре — задачи уровня Яндекс и Ozon, от простых до сложных. Без установки, сразу в браузере.