Что такое Window Functions в SQL и как с их помощью найти 3 последних сообщения для каждого пользователя в одном запросе?

## Что такое Window Functions в SQL

Оконные функции (Window Functions) — это мощный инструмент SQL, который позволяет выполнять вычисления над набором строк, связанных с текущей строкой, без группировки и потери исходных данных. В отличие от агрегатных функций (SUM, COUNT, AVG), оконные функции не сворачивают строки в одну — каждая строка остаётся в результирующем наборе, но получает дополнительное вычисленное значение.

Оконные функции появились в стандарте SQL:2003 и поддерживаются в PostgreSQL, MySQL 8+, SQL Server, Oracle и других современных СУБД.

### Синтаксис оконной функции

sql
ФУНКЦИЯ() OVER (
PARTITION BY колонка_группировки
ORDER BY колонка_сортировки
ROWS/RANGE BETWEEN …
)

— **PARTITION BY** — разбивает данные на независимые группы (окна), аналог GROUP BY, но без свёртки строк.
— **ORDER BY** — задаёт порядок строк внутри каждого окна.
— **ROWS/RANGE** — дополнительно ограничивает рамку окна.

### Основные оконные функции

— **ROW_NUMBER()** — присваивает уникальный порядковый номер каждой строке внутри окна.
— **RANK()** — ранжирует строки, пропуская номера при одинаковых значениях.
— **DENSE_RANK()** — ранжирует без пропуска номеров.
— **LAG() / LEAD()** — обращаются к предыдущей или следующей строке.
— **SUM(), AVG(), COUNT()** — агрегаты в оконном режиме.

### Задача: найти 3 последних сообщения для каждого пользователя

Предположим, есть таблица `messages`:

| id | user_id | message | created_at |
|—-|———|———|————|
| 1 | 1 | Привет | 2024-01-01 |
| 2 | 1 | Как дела? | 2024-01-02 |
| 3 | 2 | Текст | 2024-01-03 |

Решение через ROW_NUMBER():

sql
SELECT *
FROM (
SELECT
id,
user_id,
message,
created_at,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY created_at DESC
) AS rn
FROM messages
) ranked
WHERE rn <= 3;

**Как это работает:**
1. Внутренний запрос присваивает каждому сообщению номер `rn` внутри группы пользователя, начиная с 1 для самого нового.
2. Внешний запрос фильтрует только строки с `rn <= 3` — то есть три последних сообщения.

### Почему не использовать подзапрос с LIMIT?

Простой `LIMIT` работает на весь результат запроса, а не для каждой группы. Оконные функции решают эту задачу элегантно и в один проход по данным, что значительно эффективнее.

### Советы по производительности

— Создайте составной индекс по `(user_id, created_at DESC)` — это ускорит сортировку внутри окон.
— В PostgreSQL можно использовать `FETCH FIRST` вместо подзапроса через `LATERAL JOIN`.
— Для очень больших таблиц рассмотрите материализованные CTE.

Оконные функции — незаменимый инструмент для аналитических запросов, рейтингов, скользящих средних и задач типа «топ-N на группу».


Задайте вопрос нейросети

Не нашли ответ? Спросите ИИ — он подготовит развёрнутую статью.