LAST VALUE: примеры (SQL)

Использование функции LAST_VALUE для анализа данных в SQL
Раздел: Аналитические функции, Оконные
LAST_VALUE(scalar_expression OVER [PARTITION BY ] ORDER BY  [ROWS/ RANGE ]): Same as scalar_expression

Функция LAST_VALUE в MS SQL Server

Функция LAST_VALUE является оконной функцией, которая возвращает последнее значение из упорядоченного набора строк в рамках окна, определяемого предложением OVER. Она применяется в аналитических запросах для получения данных из конечной строки заданной секции без использования самообъединений.

Функция используется, когда необходим доступ к значению из последней строки упорядоченного набора данных в рамках группы. Типичные сценарии включают вычисление разницы между текущим и последним значением в периоде, определение финального статуса или получение итоговой суммы нарастающим итогом.

Синтаксис:
LAST_VALUE ( выражение ) OVER ( [ <аргументы_окна> ] )

Аргументы:

  • выражение — столбец или вычисляемое выражение, значение которого возвращается из последней строки окна. Не может быть другой оконной функцией или агрегатной функцией.
  • OVER ( [ аргументы_окна ] ) — определяет секционирование и упорядочение набора строк. Аргументы включают:
    • PARTITION BY — делит результирующий набор на секции, к которым применяется функция. Необязательный аргумент.
    • ORDER BY — определяет логический порядок строк в каждой секции. Обязателен для функции LAST_VALUE.
    • ROWS/RANGE — ограничивает строки в рамках секции, указывая начальную и конечную точки окна. По умолчанию используется RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW, что часто приводит к неожиданным результатам. Для получения истинного последнего значения обычно используют ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING.

Возвращаемое значение: Тип данных, идентичный типу переданного выражения.

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

Пример 1: Базовое использование с ORDER BY и изменением рамки окна.

SELECT
  месяц,
  выручка,
  LAST_VALUE(выручка) OVER (ORDER BY месяц) как ПоследняяЗначПоУмолчанию,
  LAST_VALUE(выручка) OVER (ORDER BY месяц ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) как ИстинноеПоследнее
FROM (VALUES 
  ('Янв', 100),
  ('Фев', 150),
  ('Мар', 200)
) as t(месяц, выручка);
месяц | выручка | ПоследняяЗначПоУмолчанию | ИстинноеПоследнее
------|---------|---------------------------|-------------------
Янв   | 100     | 100                       | 200
Фев   | 150     | 150                       | 200
Мар   | 200     | 200                       | 200

Пример 2: Использование с PARTITION BY для получения последнего значения в каждой группе.

SELECT
  отдел,
  сотрудник,
  зарплата,
  LAST_VALUE(зарплата) OVER (
    PARTITION BY отдел 
    ORDER BY сотрудник
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) as ПоследняяЗПВОтделе
FROM (VALUES
  ('IT', 'Алексей', 50000),
  ('IT', 'Мария', 55000),
  ('HR', 'Иван', 45000),
  ('HR', 'Ольга', 47000)
) as t(отдел, сотрудник, зарплата);
отдел | сотрудник | зарплата | ПоследняяЗПВОтделе
------|-----------|----------|--------------------
HR    | Иван      | 45000    | 47000
HR    | Ольга     | 47000    | 47000
IT    | Алексей   | 50000    | 55000
IT    | Мария     | 55000    | 55000

Похожие функции в MS SQL

FIRST_VALUE — возвращает первое значение в упорядоченном наборе. Применяется, когда требуется начальное значение, а не конечное.

LAG и LEAD — позволяют обращаться к данным из предыдущих или следующих строк относительно текущей на заданное смещение. Удобны для последовательного сравнения соседних строк, в то время как LAST_VALUE работает с границей всего окна.

MAX или MIN с оконным определением — при использовании с рамкой UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING могут дать аналогичный LAST_VALUE результат для вычисления экстремумов, но LAST_VALUE учитывает порядок, а не только значение.

Выбор функции: LAST_VALUE предпочтительнее, когда важен именно порядок строк и значение из конкретной позиции (последней). LAG/LEAD — для доступа к соседним строкам. FIRST_VALUE — для доступа к первой строке. Агрегатные функции — когда нужно именно максимальное или минимальное значение, независимо от порядка.

Аналоги в других СУБД

PostgreSQL, Oracle: Поддерживают идентичный синтаксис LAST_VALUE с теми же особенностями работы с рамками окна.

-- PostgreSQL/Oracle
SELECT id, value,
LAST_VALUE(value) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM sample_table;

MySQL (с версии 8.0): Также поддерживает функцию LAST_VALUE. Поведение аналогично.

-- MySQL 8.0
SELECT id, value,
LAST_VALUE(value) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) last_val
FROM sample_table;

SQLite: Не имеет встроенной LAST_VALUE. Альтернатива — использование агрегатной функции MAX() с оконным определением или подзапрос.

-- SQLite (эмуляция через MAX)
SELECT id, value,
MAX(value) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as last_val
FROM sample_table;

Основное отличие между реализациями — это возможность использования ROWS/RANGE и производительность. В MS SQL, PostgreSQL и Oracle семантика очень близка. В MySQL до версии 8.0 оконные функции отсутствовали.

Типичные ошибки

Ошибка 1: Использование ORDER BY без указания правильной рамки окна, приводящее к возврату текущего значения, а не последнего.

-- Неправильно
SELECT месяц, выручка,
LAST_VALUE(выручка) OVER (ORDER BY месяц) as Последняя
FROM продажи;
-- Для каждой строки возвращается значение выручки из текущей строки, а не из последней в наборе.

Ошибка 2: Попытка использования в WHERE или HAVING, что запрещено для оконных функций.

-- Ошибка
SELECT месяц, выручка
FROM продажи
WHERE LAST_VALUE(выручка) OVER (ORDER BY месяц) > 1000; -- Так нельзя
-- Оконные функции допускаются только в SELECT или ORDER BY.

Ошибка 3: Неоднозначное ORDER BY, приводящее к недетерминированным результатам, если несколько строк имеют одинаковые значения порядка.

SELECT id, группа, значение,
LAST_VALUE(значение) OVER (PARTITION BY группа ORDER BY id) as последнее
FROM таблица;
-- Если в одной группе несколько строк с одинаковым id, результат может быть непредсказуемым.

Изменения в последних версиях

В MS SQL Server 2012 произошло введение поддержки оконных функций, включая LAST_VALUE. С тех пор существенных изменений в синтаксисе или базовом поведении функции не было.

Основные улучшения связаны с оптимизацией выполнения оконных функций в целом в более новых версиях SQL Server (2016, 2019, 2022), что повышает производительность запросов с LAST_VALUE, особенно при использовании индексов, соответствующих порядку в предложении OVER.

В версии SQL Server 2022 продолжена работа над улучшением обработки оконных функций в пакетном режиме для сценариев с большими объемами данных.

Расширенные примеры

Пример 1: Определение финального статуса в цепочке событий для каждого объекта.

WITH События AS (
  SELECT 1 as object_id, '2023-01-01' as дата, 'Создан' as статус
  UNION ALL SELECT 1, '2023-01-02', 'В работе'
  UNION ALL SELECT 1, '2023-01-03', 'Завершен'
  UNION ALL SELECT 2, '2023-01-01', 'Создан'
  UNION ALL SELECT 2, '2023-01-05', 'В работе'
)
SELECT DISTINCT
  object_id,
  LAST_VALUE(статус) OVER (
    PARTITION BY object_id 
    ORDER BY дата
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) as итоговый_статус
FROM События;
object_id | итоговый_статус
----------|----------------
1         | Завершен
2         | В работе

Пример 2: Расчет нарастающего итога с выводом последнего значения итога как общей суммы.

SELECT
  месяц,
  выручка,
  SUM(выручка) OVER (ORDER BY месяца ROWS UNBOUNDED PRECEDING) as нарастающий_итог,
  LAST_VALUE(выручка) OVER (
    ORDER BY месяца ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) as общая_выручка_через_LAST,
  SUM(выручка) OVER () as общая_выручка_через_SUM
FROM продажи
ORDER BY месяца;

Пример 3: Использование с IGNORE NULLS (доступно с SQL Server 2022). Получение последнего непустого значения.

-- Только для SQL Server 2022 и новее
SELECT
  дата,
  показатель,
  LAST_VALUE(показатель) IGNORE NULLS OVER (ORDER BY дата) as последнее_непустое
FROM временной_ряд;

Пример 4: Комбинирование с другими оконными функциями для сложной аналитики.

SELECT
  отдел,
  сотрудник,
  продажи,
  ROW_NUMBER() OVER (PARTITION BY отдел ORDER BY продажи DESC) as место_в_отделе,
  LAST_VALUE(сотрудник) OVER (
    PARTITION BY отдел 
    ORDER BY продажи
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  ) as сотрудник_с_мин_продажами
FROM результаты_квартала;

MS SQL LAST_VALUE function comments

En
LAST VALUE Returns the last value in an ordered set of values