Оконные функции SQL: полное руководство с примерами
Разбираем ROW_NUMBER, RANK, LAG, LEAD и другие оконные функции на практических примерах из маркетинговой аналитики.
Оконные функции — один из самых мощных инструментов аналитического SQL. Они позволяют выполнять вычисления по набору строк, связанных с текущей строкой, без группировки данных.
Что такое оконные функции?
В отличие от агрегатных функций, оконные функции не схлопывают строки. Каждая строка сохраняет свою индивидуальность, но получает доступ к данным «окна» — набора связанных строк.
SELECT user_id, order_date, amount, SUM(amount) OVER ( PARTITION BY user_id ORDER BY order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS running_totalFROM orders;ROW_NUMBER, RANK и DENSE_RANK
Эти функции нумеруют строки внутри каждого окна:
SELECT campaign_name, ad_spend, conversions, ROW_NUMBER() OVER (ORDER BY conversions DESC) AS rn, RANK() OVER (ORDER BY conversions DESC) AS rnk, DENSE_RANK() OVER (ORDER BY conversions DESC) AS dense_rnkFROM campaignsWHERE month = '2026-03';Разница между ними:
ROW_NUMBER()— уникальный номер для каждой строкиRANK()— одинаковый ранг для равных значений, следующий ранг пропускаетсяDENSE_RANK()— одинаковый ранг для равных значений, без пропусков
Попробуй сам — вот рабочие данные:
Попробуй: ROW_NUMBER, RANK, DENSE_RANK
LAG и LEAD: сравниваем с предыдущими периодами
Это must-have для маркетинговой аналитики. Представьте: нужно сравнить конверсию каждого дня с предыдущим.
SELECT event_date, sessions, conversions, LAG(conversions, 1) OVER (ORDER BY event_date) AS prev_day_conv, conversions - LAG(conversions, 1) OVER (ORDER BY event_date) AS conv_diff, ROUND( (conversions - LAG(conversions, 1) OVER (ORDER BY event_date))::numeric / NULLIF(LAG(conversions, 1) OVER (ORDER BY event_date), 0) * 100, 1 ) AS conv_change_pctFROM daily_metricsWHERE event_date >= CURRENT_DATE - INTERVAL '30 days';Попробуй: LAG — сравнение с прошлым днём
NTILE: сегментация пользователей
NTILE делит строки на N равных групп — идеально для RFM-анализа:
SELECT user_id, total_revenue, last_order_date, order_count, NTILE(5) OVER (ORDER BY total_revenue DESC) AS revenue_quintile, NTILE(5) OVER (ORDER BY last_order_date DESC) AS recency_quintile, NTILE(5) OVER (ORDER BY order_count DESC) AS frequency_quintileFROM user_metrics;Практический кейс: когортный анализ
Соберём когортный анализ retention пользователей с помощью оконных функций:
WITH user_cohorts AS ( SELECT user_id, MIN(DATE_TRUNC('month', first_visit_date)) AS cohort_month FROM users GROUP BY user_id),monthly_activity AS ( SELECT uc.cohort_month, DATE_TRUNC('month', e.event_date) AS activity_month, COUNT(DISTINCT e.user_id) AS active_users FROM events e JOIN user_cohorts uc ON e.user_id = uc.user_id GROUP BY 1, 2)SELECT cohort_month, activity_month, active_users, FIRST_VALUE(active_users) OVER ( PARTITION BY cohort_month ORDER BY activity_month ) AS cohort_size, ROUND( active_users::numeric / FIRST_VALUE(active_users) OVER ( PARTITION BY cohort_month ORDER BY activity_month ) * 100, 1 ) AS retention_pctFROM monthly_activityORDER BY cohort_month, activity_month;Итого
Оконные функции экономят десятки строк кода и делают запросы в разы читаемее. Для маркетинговой аналитики это незаменимый инструмент — от расчёта retention до построения RFM-сегментации.
В следующих постах разберём продвинутые паттерны: скользящие средние, cumulative metrics и frame specifications.
Нравится контент? Подписывайся на Telegram
SQL-задачки, кейсы из практики и закулисье аналитики.
Похожие статьи
Анализ воронки на чистом SQL: от визита до покупки
Строим полный анализ воронки конверсий в ClickHouse: расчёт CR между шагами, поиск узких мест, когортные воронки.
UTM-разметка: почему у тебя бардак в аналитике и как это починить
Разбираем типичные ошибки UTM-разметки, даём готовую конвенцию именования и SQL-запрос для очистки данных в ClickHouse.
Настройка электронной коммерции в Яндекс Метрике: пошаговый гайд
Как правильно настроить ecommerce-отслеживание в Яндекс Метрике и начать анализировать воронку покупок.