Функции FIRST_VALUE() и LAST_VALUE() в SQL: первое и последнее значение в окне

FIRST_VALUE() и LAST_VALUE() — оконные функции, которые возвращают первое и последнее значение в рамке окна. Их используют, чтобы к каждой строке подтянуть, например, сумму первого заказа пользователя или последнее событие сессии. Главная ловушка — LAST_VALUE: по умолчанию рамка окна заканчивается на текущей строке, поэтому без явного фрейма функция вернёт значение текущей строки, а не последней в группе. Поддерживаются в PostgreSQL, MySQL 8+ и SQLite 3.25+.

Синтаксис

FIRST_VALUE(выражение) OVER (PARTITION BY ... ORDER BY ... [ROWS BETWEEN ... AND ...])

Примеры использования

Пример 1: Сумма первого заказа пользователя

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

Пример 2: Последний заказ — с правильной рамкой

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

Пример 3: Разница текущего заказа с первым

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-задачи в тренажёре →

Типичная ошибка

У LAST_VALUE рамка по умолчанию RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, поэтому она вернёт текущую строку — почти всегда нужно явно писать ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING. IGNORE NULLS в PostgreSQL и MySQL не поддерживается, так что NULL в начале окна попадёт в результат FIRST_VALUE.

Где спрашивают на собеседовании

Нужны для first-touch атрибуции, первого/последнего события сессии или заказа. На собесе почти всегда спрашивают про ловушку рамки LAST_VALUE. Идут в паре с PARTITION BY, ORDER BY и явным фреймом ROWS.

FAQ: частые вопросы про FIRST_VALUE / LAST_VALUE

Почему LAST_VALUE() возвращает не последнее значение, а текущую строку?

Из-за рамки окна по умолчанию: она заканчивается на текущей строке (CURRENT ROW). Чтобы увидеть настоящее последнее значение группы, добавьте фрейм ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.

Чем FIRST_VALUE() отличается от MIN()?

FIRST_VALUE() берёт значение первой строки по заданному ORDER BY окна, а MIN() — наименьшее значение независимо от порядка. Например, FIRST_VALUE по дате даст первый по времени заказ, даже если его сумма не минимальная.

Работают ли FIRST_VALUE() и LAST_VALUE() в SQLite?

Да, начиная с SQLite 3.25 (2018), где появились оконные функции. Также поддерживаются в PostgreSQL и MySQL 8+. А вот опция IGNORE NULLS в PostgreSQL и MySQL недоступна.

Задачи в тренажёре по теме

Вопросы с собеседований по теме

Смежные темы SQL

Оконные функции · Ранжирование

Другие функции SQL

ROW_NUMBER · RANK · DENSE_RANK · LAG · LEAD · NTILE

Открыть SQL-тренажёр (565 задач) →