Оконные функции

Нарастающий итог: OVER с ORDER BY

Содержание курса

В прошлом уроке OVER с PARTITION BY вычислял агрегат сразу по всему разделу — каждая строка получала одно и то же итоговое значение для своей категории. Сейчас добавим ORDER BY внутрь OVER, и поведение изменится принципиально: функция начнёт учитывать, где именно в отсортированном ряду находится текущая строка.

ORDER BY внутри OVER и неявная RANGE-рамка: почему одинаковые даты дают скачок суммы

Как только внутри OVER появляется ORDER BY, оконная функция перестаёт смотреть на весь раздел сразу. Теперь у неё есть понятие «позиция текущей строки», и по умолчанию SQLite применяет рамку RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Слово RANGE здесь ключевое: рамка определяется не количеством строк, а значением ключа сортировки.

Что это значит на практике? Если несколько строк имеют одинаковое значение ключа — например, одну и ту же дату — все они считаются «текущими» одновременно. В рамку для каждой из них входит одинаковый набор строк, и все они получают одинаковый результат.

Разберём на конкретных данных. Пусть таблица sales содержит три строки:

CREATE TABLE sales (sale_id INTEGER PRIMARY KEY, sale_date TEXT, amount INTEGER);
INSERT INTO sales VALUES
  (1, '2024-01-01', 100),
  (2, '2024-01-01', 200),
  (3, '2024-01-02', 300);

Запрос с неявной RANGE-рамкой:

SELECT sale_id, sale_date, amount,
       SUM(amount) OVER (ORDER BY sale_date) AS running_total
FROM sales
ORDER BY sale_date, sale_id;

Результат:

sale_id sale_date amount running_total
1 2024-01-01 100 300
2 2024-01-01 200 300
3 2024-01-02 300 600

Строки 1 и 2 имеют одинаковую дату. RANGE объединяет их в одну «группу» с точки зрения рамки, и обе получают сумму 100 + 200 = 300 — скачок сразу на два шага. Строка 3 получает уже 300 + 300 = 600.

Это не ошибка SQL и не случайность — именно так определена семантика RANGE. Проблема возникает тогда, когда ожидаешь, что нарастающий итог будет расти на одно значение за строку. При дублирующихся ключах сортировки RANGE этого не даёт: промежуточные значения пропадают, и результат выглядит как «прыжок».

Начать заново?

Помощник начнёт новый диалог по этой задаче. Текущая переписка останется в истории до конца срока хранения.

О переписке

Можно приложить до трёх фото PNG, JPEG или WebP по 5 МБ.

Переписка и фото хранятся 90 дней после последнего принятого сообщения. Преподаватель получит доступ к диалогу, когда вы попросите его помочь.

Проверка решения остаётся отдельным действием.