В процессе разработки отчетов и обработки данных в 1С:Предприятии часто возникает задача вычисления нарастающего (накопительного) итога. Например, когда для каждой следующей строки требуется получить сумму текущего значения и всех предыдущих записей в упорядоченном наборе данных.
В отличие от стандартного SQL, в языке запросов платформы 1С отсутствуют оконные функции (такие как конструкции с OVER(ORDER BY...)). Рассмотрим подробнее, какими способами можно решить эту задачу на уровне запроса, средствами Системы компоновки данных (СКД) и с помощью встроенного языка 1С.
Разберем классический подход на уровне языка запросов 1С. Для получения нарастающего итога исходная таблица соединяется сама с собой с использованием условия неравенства (>= или <= в зависимости от направления сортировки), после чего выполняется группировка.
Посмотрим на пример практической реализации:
ВЫБРАТЬ
Исход.А КАК А,
СУММА(Исход1.Б) КАК Б
ИЗ
Исход КАК Исход
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Исход КАК Исход1
ПО Исход.А >= Исход1.А
СГРУППИРОВАТЬ ПО
Исход.А
УПОРЯДОЧИТЬ ПО
А
Проанализируем механику работы этого запроса:
Исход выступает базовым набором строк.Исход присоединяются все строки из копии Исход1, у которых значение поля А меньше или равно значению поля А текущей строки.Исход.А и применение агрегатной функции СУММА(Исход1.Б) позволяют сложить все выбранные предшествующие значения для каждого шага.Важные ограничения и особенности производительности:
O(N^2). На небольших объемах (несколько десятков или сотен строк) запрос отработает быстро, однако на таблицах в тысячи строк и более это вызовет резкое падение производительности и перегрузку СУБД.ИНДЕКСИРОВАТЬ ПО.>= объединит одинаковые строки в один общий итог на каждом дубле. Чтобы избежать искажения сумм, необходимо формировать уникальный ключ упорядочивания (например, комбинацию даты, момента времени или ссылки документа).Если задача решается в рамках отчета на Системе компоновки данных (СКД), строить тяжелые запросы с самосоединением не требуется. СКД содержит оптимизированные встроенные функции для работы с ресурсами.
Рассмотрим варианты выражений на вкладке Ресурсы схемы компоновки данных:
ВычислитьФрагмент("Сумма(Б)", "ПерваяСтрока()", "ТекущаяСтрока()")
Данное выражение вычисляет агрегат по ресурсу Б от первой строки до текущей включительно.
Вычислить("Сумма(Б)", , "ПоВключаяТекущую")
Указание третьего параметра ПоВключаяТекущую указывает системе накапливать сумму по мере обхода группировок без лишней нагрузки на базу данных.
Когда данные выбираются для алгоритмической обработки в коде (например, при проведении документов или заполнении табличных частей), наиболее эффективным подходом является получение отсортированного списка простым запросом с последующим обходом в цикле.
Разберем по шагам реализацию с линейной сложностью O(N):
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Исход.А КАК А,
| Исход.Б КАК Б
|ИЗ
| ВТ_ИсходныеДанные КАК Исход
|УПОРЯДОЧИТЬ ПО
| Исход.А";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
НакопленныйИтог = 0;
Пока Выборка.Следующий() Цикл
НакопленныйИтог = НакопленныйИтог + Выборка.Б;
// Использование полученного значения НакопленныйИтог для текущей строки
// Например, запись в результирующую таблицу значений или массив
КонецЦикла;
Такой способ гарантирует высокую скорость выполнения даже на десятках и сотнях тысяч записей, так как СУБД выполняет только легкую сортировку, а сложение происходит в один проход в оперативной памяти сервера 1С.