Короткий ответ
Оконные функции вычисляют значение для каждой строки на основе «окна» связанных строк (OVER (PARTITION BY ... ORDER BY ...)), не сворачивая результат в одну строку на группу, в отличие от обычных агрегатных функций с GROUP BY; ROW_NUMBER() нумерует строки внутри партиции, RANK() делает то же самое, но учитывает повторы значений при сортировке.
Как это работает подробнее
PARTITION BY делит результат на логические группы без потери отдельных строк, ORDER BY внутри OVER определяет порядок для вычисления ранга/номера/смещения; функция вычисляется для каждой исходной строки.
Пример кода
example.sql
SELECT id, user_id, amount, created_at,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders;
-- Выбрать только последний заказ каждого пользователя:
SELECT * FROM (
SELECT id, user_id, amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
) t WHERE rn = 1;Что использовать на практике
- Практика. Типичные задачи — выбор «последней записи на пользователя» (ROW_NUMBER() + фильтр = 1), топ-N в каждой категории, скользящие суммы/средние — это часто проще и быстрее, чем аналогичная логика через self-join.
Подводные камни
- Ошибка. Путают RANK() и ROW_NUMBER() — при одинаковых значениях ORDER BY RANK() присвоит одинаковый ранг нескольким строкам и «пропустит» следующие номера, а ROW_NUMBER() всегда даёт уникальный последовательный номер.