К статье
Практический материал

Разработка и Конфигурация · 11 мин

Как сделать запрос с нарастающим итогом в 1С

Как сделать запрос с нарастающим итогом в 1С: через ИТОГИ, самосоединение таблиц и СКД. Примеры кода, типичные ошибки, проверка результата и ограничения.

Нарастающий итог (накопительная сумма) — это сумма, которая увеличивается от строки к строке: для каждой записи показывается сумма всех предыдущих значений плюс текущее. В учётных задачах это нужно для расчёта остатков на каждую дату, контроля лимитов, план-фактного анализа и построения отчётов по регистрам накопления.

В языке запросов 1С нет встроенной оконной функции вида «сумма с накоплением», как в классическом SQL. Поэтому задача решается одним из трёх рабочих способов: через секцию ИТОГИ с иерархией, через самосоединение таблицы с условием «меньше или равно» или средствами СКД с вычисляемым полем. Выбор зависит от того, где будет использоваться результат — в программном коде, в отчёте или в динамическом списке.

Ниже разобраны все три подхода с примерами кода, условиями применимости, ограничениями по производительности и способами проверки корректности результата.

Что такое нарастающий итог и когда он нужен

Нарастающий итог — это последовательность, где каждое значение равно сумме текущего показателя и всех предыдущих. Например, для оборотов по дням 100, 200, 150 нарастающий итог даст 100, 300, 450. В терминах SQL это оконная функция SUM(...) OVER (ORDER BY ...).

Типовые сценарии в 1С: остаток товара на конец каждого дня, накопленная сумма оплат по договору, контроль исполнения бюджета с начала года, расчёт пеней и процентов. Часто задача маскируется: данные уже хранятся в регистре накопления с видом «Остатки», и тогда нарастающий итог не нужен вовсе — достаточно виртуальной таблицы остатков.

Способ 1: секция ИТОГИ и обход выборки

Самый простой вариант, если результат обрабатывается в коде. Запрос группирует данные по нужному измерению (например, по периоду), а секция ИТОГИ даёт общую сумму. Нарастающее значение накапливается переменной в цикле обхода выборки.

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Продажи.Период КАК Период,

| СУММА(Продажи.СуммаОборот) КАК Сумма

|ИЗ

| РегистрНакопления.Продажи.Обороты(, , День, ) КАК Продажи

|СГРУППИРОВАТЬ ПО

| Продажи.Период

|УПОРЯДОЧИТЬ ПО

| Период

|ИТОГИ

| СУММА(Сумма)

|ПО

| ОБЩИЕ";

НарастающийИтог = 0;

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

Если Выборка.ТипЗаписи() = ТипЗаписиЗапроса.ДетальнаяЗапись Тогда

НарастающийИтог = НарастающийИтог + Выборка.Сумма;

// НарастающийИтог — накопленная сумма на текущую строку

КонецЕсли;

КонецЦикла;

Ключевые условия корректной работы: сортировка по периоду обязательна (без УПОРЯДОЧИТЬ ПО порядок строк не гарантирован), накопление выполняется только для детальных записей, а итоговые записи пропускаются через проверку ТипЗаписи(). Если нужен нарастающий итог внутри групп (например, по каждому контрагенту отдельно), переменную сбрасывают при смене группировочной записи.

Ограничение способа: результат существует только в коде, его нельзя напрямую вывести в отчёт или поместить в таблицу значений запросом. Для отчётности лучше подходят следующие два варианта.

Способ 2: самосоединение таблицы в запросе

Классический приём, когда нарастающий итог нужен прямо в результате запроса: таблица соединяется сама с собой по условию «период второй копии меньше или равен периоду первой», а затем суммируется с группировкой.

ВЫБРАТЬ

Т1.Период КАК Период,

СУММА(Т2.Сумма) КАК НарастающийИтог

ПОМЕСТИТЬ Результат

ИЗ

ОборотыПоДням КАК Т1

ЛЕВОЕ СОЕДИНЕНИЕ ОборотыПоДням КАК Т2

ПО Т2.Период <= Т1.Период

СГРУППИРОВАТЬ ПО

Т1.Период

УПОРЯДОЧИТЬ ПО

Период

Здесь ОборотыПоДням — временная таблица, подготовленная предыдущим пакетом запроса (группировка оборотов по дням). Для каждой строки Т1 соединение подтягивает все строки Т2 с периодом не позже текущего, и их сумма даёт накопленное значение. Если нужен нарастающий итог в разрезе измерения, условие соединения дополняют равенством измерения: Т2.Контрагент = Т1.Контрагент И Т2.Период <= Т1.Период.

Условие «меньше или равно» должно быть именно нестрогим (<=), иначе первая строка потеряет собственное значение. Если периоды могут повторяться (несколько документов в одну секунду), добавьте в условие соединения дополнительный упорядочивающий признак, например сравнение по моменту времени или ссылке, иначе строки с одинаковым периодом получат одинаковый итог.

Способ 3: нарастающий итог в СКД

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

Практический порядок для отчёта «Остатки на каждую дату»: запрос выбирает обороты по дням; в ресурсах задаётся выражение Сумма(СуммаОборот) с расчётом по нужной группировке; в настройках отчёта добавляется группировка по периоду с упорядочиванием по возрастанию; для вывода накопленного значения используется вычисляемое поле или функция языка выражений СКД.

Порядок действий в СКД
  1. 1
    Создайте набор данных с оборотами, сгруппированными по периоду
  2. 2
    На закладке «Ресурсы» добавьте суммовое поле с выражением Сумма(Поле)
  3. 3
    Укажите группировку расчёта и включите упорядочивание по периоду в настройках
  4. 4
    Добавьте вычисляемое поле для накопленного значения при необходимости
  5. 5
    Сформируйте отчёт и сверьте последнее значение с общим итогом

Преимущество СКД — результат сразу доступен пользователю в отчёте, настройки можно менять без доработки кода. Недостаток — поведение зависит от настроек группировок, и при сложной структуре отчёта нарастающий итог может «сбиваться» при добавлении новых группировок пользователем.

Сравнение способов и критерии выбора

Сравнение вариантов

ИТОГИ + обход в коде

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

Самосоединение в запросе

Даёт готовую таблицу с итогом одним запросом. Не подходит для больших объёмов из-за квадратичной сложности

СКД с ресурсом и группировкой

Оптимально для отчётов, настройки меняются без кода. Требует аккуратной настройки группировок и упорядочивания

Регистр остатков вместо расчёта

Если данные уже есть в регистре с видом «Остатки», нарастающий итог не нужен — берите виртуальную таблицу остатков

Правило выбора простое: сначала проверьте, нельзя ли взять готовые остатки из регистра — это самый быстрый и надёжный путь. Если накопление нужно посчитать, для отчёта используйте СКД, для пакетной обработки — обход выборки, а самосоединение оставьте для небольших выборок и промежуточных расчётов.

Маршрут решения

Как выбрать действие

Данные хранятся в регистре накопления с видом «Остатки»Используйте виртуальную таблицу Остатки вместо расчёта нарастающего итога
Нужен отчёт для пользователя с гибкими настройкамиДелайте нарастающий итог средствами СКД
Результат обрабатывается программно, строк многоИспользуйте ИТОГИ и накопление в цикле обхода выборки
Строк меньше нескольких тысяч и нужен один запросДопустимо самосоединение с условием по периоду

Проверка результата и типичные ошибки

Главный контрольный признак: последнее значение нарастающего итога должно совпадать с общей суммой по всей выборке. Если они расходятся — нарушено условие соединения, потеряны строки или сбит порядок. Второй признак: нарастающий итог никогда не уменьшается, если все исходные значения положительные; убывание при положительных данных означает ошибку сортировки.

Контрольные точки

Как проверить результат

Последняя строка итога
Норма

Равна общей сумме по выборке

Тревожный признак

Меньше или больше общей суммы — потеряны или задвоены строки

Монотонность значений
Норма

Итог не убывает при положительных данных

Тревожный признак

Значения скачут — нарушено упорядочивание по периоду

Первая строка
Норма

Итог равен значению самой строки

Тревожный признак

Итог равен нулю — использовано строгое условие < вместо <=

Разрезы измерений
Норма

Итог считается отдельно внутри каждого измерения

Тревожный признак

Итог «перетекает» между контрагентами или складами

Типичные ошибки
01
Строгое условие Т2.Период < Т1.Период — первая строка получает нулевой итог
02
Отсутствие УПОРЯДОЧИТЬ ПО — порядок строк не определён, итог становится случайным
03
Накопление по итоговым записям без проверки ТипЗаписи() — сумма удваивается
04
Самосоединение по таблице без предварительной группировки — дубли периодов дают одинаковые итоги для разных строк
05
Забытое условие равенства измерения — итог смешивается между разными объектами учёта

Отдельная ошибка — попытка посчитать нарастающий итог по регистру остатков через обороты за всю историю. Это долго и избыточно: виртуальная таблица Остатки или ОстаткиИОбороты решает задачу штатно, с учётом переноса остатков и отборов.

Ограничения и производительность

Язык запросов 1С не поддерживает оконные функции, поэтому любое решение — компромисс. Самосоединение на больших объёмах создаёт декартово произведение промежуточных строк и может привести к тайм-ауту на сервере СУБД. Обход выборки масштабируется линейно, но требует кода и не работает внутри отчёта без программного вывода.

Для больших отчётов полезно ограничивать период и набор измерений ещё на этапе временной таблицы: чем меньше строк попадает в самосоединение или в набор СКД, тем быстрее расчёт. Индексы по периоду и измерениям регистра платформа использует автоматически, но временные таблицы стоит явно индексировать через ИНДЕКСИРОВАТЬ ПО, если они участвуют в соединениях.

Итог

Что важно запомнить

Оконных функций в запросах 1С нет. Нарастающий итог делают через ИТОГИ с циклом, самосоединение с условием «<=» или ресурсы СКД. Всегда проверяйте: последняя строка равна общей сумме, итог не убывает, первая строка не обнулена. Для остатков используйте штатные виртуальные таблицы вместо ручного расчёта.

Частые вопросы

Можно ли сделать нарастающий итог одним запросом без кода?

Да, через самосоединение таблицы с условием «период меньше или равен» и группировкой. Это работает в консоли запросов и в наборах данных СКД, но на больших объёмах запрос замедляется из-за квадратичного числа сравнений.

Почему первая строка нарастающего итога равна нулю?

В условии соединения использовано строгое сравнение «меньше» вместо «меньше или равно». Замените условие на Т2.Период <= Т1.Период, и первая строка получит собственное значение.

Как посчитать нарастающий итог отдельно по каждому контрагенту?

Добавьте в условие соединения равенство измерения: Т2.Контрагент = Т1.Контрагент. При обходе выборки в коде сбрасывайте накопленную переменную при смене группировочной записи.

Чем нарастающий итог отличается от остатков регистра?

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

Почему запрос с самосоединением выполняется очень долго?

Соединение «каждый с каждым до текущей строки» порождает число сравнений порядка квадрата количества строк. Сократите период, сгруппируйте данные заранее во временную таблицу с индексом или перенесите расчёт в обход выборки либо в СКД.

Можно ли вывести нарастающий итог в динамическом списке?

Напрямую — нет, динамический список строится на одном запросе без возможности накопления между строками. Варианты: произвольный запрос с самосоединением (с потерей части штатных возможностей списка) или вывод через отчёт на СКД.