SELECT
user_id,
order_date,
amount,
FIRST_VALUE(amount) OVER (
PARTITION BY user_id ORDER BY order_date
) AS first_order_amount
FROM orders;
Результат:
user_id | order_date | amount | first_order_amount --------+------------+---------+------------------- 8 | 2026-01-12 | 500.00 | 500.00 8 | 2026-03-04 | 1240.00 | 500.00
SELECT
user_id,
order_date,
amount,
LAST_VALUE(amount) OVER (
PARTITION BY user_id ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_order_amount
FROM orders;
Результат:
user_id | order_date | amount | last_order_amount --------+------------+---------+------------------ 8 | 2026-01-12 | 500.00 | 1240.00 8 | 2026-03-04 | 1240.00 | 1240.00
SELECT
user_id,
order_date,
amount - FIRST_VALUE(amount) OVER (
PARTITION BY user_id ORDER BY order_date
) AS diff_from_first
FROM orders;
Результат:
user_id | order_date | diff_from_first --------+------------+---------------- 8 | 2026-01-12 | 0.00 8 | 2026-03-04 | 740.00Порешать SQL-задачи в тренажёре →
Нужны для first-touch атрибуции, первого/последнего события сессии или заказа. На собесе почти всегда спрашивают про ловушку рамки LAST_VALUE. Идут в паре с PARTITION BY, ORDER BY и явным фреймом ROWS.
Из-за рамки окна по умолчанию: она заканчивается на текущей строке (CURRENT ROW). Чтобы увидеть настоящее последнее значение группы, добавьте фрейм ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.
FIRST_VALUE() берёт значение первой строки по заданному ORDER BY окна, а MIN() — наименьшее значение независимо от порядка. Например, FIRST_VALUE по дате даст первый по времени заказ, даже если его сумма не минимальная.
Да, начиная с SQLite 3.25 (2018), где появились оконные функции. Также поддерживаются в PostgreSQL и MySQL 8+. А вот опция IGNORE NULLS в PostgreSQL и MySQL недоступна.
Оконные функции · Ранжирование
ROW_NUMBER · RANK · DENSE_RANK · LAG · LEAD · NTILE
Открыть SQL-тренажёр (565 задач) →