SQL для продакт-менеджера с нуля
Содержание:
Зачем SQL продакту
Продакт без SQL зависит от аналитика для каждого ad hoc-вопроса. С SQL — может за час сам проверить гипотезу, посмотреть статистику фичи, разобрать конкретный кейс.
На собесе SQL спрашивают почти везде — банки, маркетплейсы, IT-продукты. Уровень — base+ (JOIN, GROUP BY, оконки). Глубокую оптимизацию для PM не спросят, это территория аналитика.
Минимум для junior PM
- SELECT, WHERE, ORDER BY, LIMIT
- JOIN (LEFT, INNER) и базовое понимание разницы
- GROUP BY и агрегаты (COUNT, SUM, AVG)
- Оконные функции (ROW_NUMBER, SUM OVER) на узнавание
- CTE (WITH) для читаемости
SELECT и WHERE
SELECT user_id, country, signup_date
FROM users
WHERE country = 'RU'
AND signup_date >= '2026-01-01'
ORDER BY signup_date DESC
LIMIT 100;WHERE фильтрует строки. Условия комбинируются через AND/OR. Сравнения дат — обычные операторы (>=, BETWEEN, <).
NULL обрабатывается отдельно — column IS NULL, не column = NULL.
JOIN
LEFT JOIN — все строки из левой таблицы + соответствующие из правой. Если справа нет — NULL.
INNER JOIN — только строки, где есть совпадение в обеих таблицах.
SELECT u.user_id, u.country, COUNT(o.order_id) AS orders_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.user_id
GROUP BY u.user_id, u.country;Если использовать INNER JOIN, юзеры без заказов выпадут из результата.
Главная ловушка: WHERE-условие на правой таблице после LEFT JOIN превращает его в INNER. Если хотите оставить NULL — фильтр в ON.
GROUP BY и агрегаты
GROUP BY группирует строки по колонке. Все колонки в SELECT либо группируются, либо агрегируются.
SELECT
country,
COUNT(*) AS users_count,
AVG(age) AS avg_age,
SUM(revenue) AS total_revenue
FROM users
GROUP BY country
HAVING COUNT(*) > 100;HAVING фильтрует группы (после агрегации). WHERE — фильтрует строки до агрегации.
Полезные агрегаты:
COUNT(*)— все строкиCOUNT(DISTINCT user_id)— уникальные значенияSUM,AVG,MIN,MAXCOUNT(*) FILTER (WHERE ...)— условный счёт (Postgres)
Оконные функции
Считают агрегат по группе, но возвращают результат для каждой строки (не сжимают в одну).
SELECT
user_id,
order_date,
amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_date) AS order_num,
SUM(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS cumulative_spent
FROM orders;ROW_NUMBER() нумерует заказы внутри клиента. SUM() OVER даёт running total.
Для PM хватит знать: ROW_NUMBER, RANK, SUM OVER, LAG, LEAD.
Что спрашивают на собесе
Топ-задачи для PM:
- Топ-10 клиентов по выручке за месяц — простой GROUP BY + ORDER BY + LIMIT
- Какие юзеры сделали первый заказ в феврале — фильтр по MIN(order_date)
- Конверсия из регистрации в покупку — JOIN users + orders, COUNT с FILTER
- Retention 7-дневный — найти юзеров активных в день 0 и день 7
- Когорты — DATE_TRUNC регистраций, JOIN с активностью, GROUP BY когорт + день
На собесе оценят не идеальный SQL, а ход мысли: задаёт ли уточняющие вопросы, умеет ли декомпозировать, читает ли свой код.
Частые ошибки
WHERE на правой таблице после LEFT JOIN. Превращает в INNER JOIN. Используйте условие в ON.
SELECT все колонки + GROUP BY одной. Ошибка SQL — нужно либо группировать все, либо использовать агрегаты.
Считать дубликаты. COUNT(*) считает все строки, включая дубликаты. Используйте COUNT(DISTINCT user_id) для уникальных.
Integer division. 5/20*100 в Postgres = 0. 5::NUMERIC / 20 * 100 = 25.
Не использовать NULLIF. value/denominator → деление на ноль уронит запрос. Безопасно: value / NULLIF(denominator, 0).
FAQ
Какой диалект SQL учить?
Postgres — самый распространённый в РФ. Если идёте в маркетплейс или telecom — ClickHouse. Принципы 80% общие.
Сколько времени учить SQL до уровня PM?
При активной практике — 2-4 недели. Решать задачи каждый день по 30-60 минут.
Это официальная информация?
Нет. Статья основана на индустриальных практиках.