Как рассчитать накопительный итог в запросе 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки отчетов и обработки данных в 1С:Предприятии часто возникает задача вычисления нарастающего (накопительного) итога. Например, когда для каждой следующей строки требуется получить сумму текущего значения и всех предыдущих записей в упорядоченном наборе данных.

В отличие от стандартного SQL, в языке запросов платформы 1С отсутствуют оконные функции (такие как конструкции с OVER(ORDER BY...)). Рассмотрим подробнее, какими способами можно решить эту задачу на уровне запроса, средствами Системы компоновки данных (СКД) и с помощью встроенного языка 1С.

Способ 1. Расчет накопительного итога через самосоединение в запросе

Разберем классический подход на уровне языка запросов 1С. Для получения нарастающего итога исходная таблица соединяется сама с собой с использованием условия неравенства (>= или <= в зависимости от направления сортировки), после чего выполняется группировка.

Посмотрим на пример практической реализации:


ВЫБРАТЬ
    Исход.А КАК А,
    СУММА(Исход1.Б) КАК Б
ИЗ
    Исход КАК Исход
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Исход КАК Исход1
        ПО Исход.А >= Исход1.А
СГРУППИРОВАТЬ ПО
    Исход.А
УПОРЯДОЧИТЬ ПО
    А

Проанализируем механику работы этого запроса:

  1. Таблица Исход выступает базовым набором строк.
  2. Для каждой строки таблицы Исход присоединяются все строки из копии Исход1, у которых значение поля А меньше или равно значению поля А текущей строки.
  3. Группировка по полю Исход.А и применение агрегатной функции СУММА(Исход1.Б) позволяют сложить все выбранные предшествующие значения для каждого шага.

Важные ограничения и особенности производительности:

  1. Вычислительная сложность: Соединение по неравенству порождает сложность порядка O(N^2). На небольших объемах (несколько десятков или сотен строк) запрос отработает быстро, однако на таблицах в тысячи строк и более это вызовет резкое падение производительности и перегрузку СУБД.
  2. Индексация: Если данные предварительно помещаются во временную таблицу, поле упорядочивания обязательно должно быть проиндексировано с помощью конструкции ИНДЕКСИРОВАТЬ ПО.
  3. Проблема неуникальных значений: Если поле упорядочивания содержит дубликаты, условие >= объединит одинаковые строки в один общий итог на каждом дубле. Чтобы избежать искажения сумм, необходимо формировать уникальный ключ упорядочивания (например, комбинацию даты, момента времени или ссылки документа).

Способ 2. Штатный расчет нарастающего итога в СКД

Если задача решается в рамках отчета на Системе компоновки данных (СКД), строить тяжелые запросы с самосоединением не требуется. СКД содержит оптимизированные встроенные функции для работы с ресурсами.

Рассмотрим варианты выражений на вкладке Ресурсы схемы компоновки данных:

  1. Использование функции ВычислитьФрагмент: ВычислитьФрагмент("Сумма(Б)", "ПерваяСтрока()", "ТекущаяСтрока()")

    Данное выражение вычисляет агрегат по ресурсу Б от первой строки до текущей включительно.

  2. Использование функции Вычислить: Вычислить("Сумма(Б)", , "ПоВключаяТекущую")

    Указание третьего параметра ПоВключаяТекущую указывает системе накапливать сумму по мере обхода группировок без лишней нагрузки на базу данных.

Способ 3. Линейный расчет на встроенном языке 1С

Когда данные выбираются для алгоритмической обработки в коде (например, при проведении документов или заполнении табличных частей), наиболее эффективным подходом является получение отсортированного списка простым запросом с последующим обходом в цикле.

Разберем по шагам реализацию с линейной сложностью O(N):


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Исход.А КАК А,
    |   Исход.Б КАК Б
    |ИЗ
    |   ВТ_ИсходныеДанные КАК Исход
    |УПОРЯДОЧИТЬ ПО
    |   Исход.А";

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

НакопленныйИтог = 0;

Пока Выборка.Следующий() Цикл
    НакопленныйИтог = НакопленныйИтог + Выборка.Б;
    
    // Использование полученного значения НакопленныйИтог для текущей строки
    // Например, запись в результирующую таблицу значений или массив
КонецЦикла;

Такой способ гарантирует высокую скорость выполнения даже на десятках и сотнях тысяч записей, так как СУБД выполняет только легкую сортировку, а сложение происходит в один проход в оперативной памяти сервера 1С.

← На главную