В процессе разработки отчетов на Системе компоновки данных (СКД) часто возникает задача получения накопительных итогов. Типичный пример: необходимо вывести список поездок вагонов за период, и для каждой строки (на каждую дату поездки) показать, какой по счету это был выезд, начиная с определенной исторической даты. При этом расчет должен идти индивидуально для каждого вагона.
В данной статье мы разберем несколько профессиональных методов решения этой задачи: от классического самосоединения в запросе до продвинутых функций языка выражений СКД и связей наборов данных. Проанализируем ситуацию, когда данные берутся из регистра сведений, и выясним, как оптимизировать расчеты при больших объемах информации.
Это наиболее универсальный метод, который работает на уровне СУБД. Суть его заключается в том, что мы соединяем таблицу саму с собой по условию соответствия объекта (вагона) и временному интервалу (все записи, дата которых меньше или равна текущей).
Рассмотрим по шагам, как составить такой запрос:
ВТ_Данные.ВТ_Данные (как Т1) с этой же таблицей ВТ_Данные (как Т2).Т1.Вагон = Т2.Вагон И Т1.Период >= Т2.Период.Т1, а для полей таблицы Т2 применим агрегатную функцию СУММА или КОЛИЧЕСТВО.Посмотрим на пример кода такого запроса:
ВЫБРАТЬ
Рег.Вагон КАК Вагон,
Рег.Период КАК Период,
1 КАК КоличествоПоездок
ПОМЕСТИТЬ ВТ_Ходки
ИЗ
РегистрСведений.ДвиженияВагонов КАК Рег
ГДЕ
Рег.Период МЕЖДУ &НачалоПериода И &КонецПериода;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Т1.Вагон КАК Вагон,
Т1.Период КАК Период,
СУММА(Т2.КоличествоПоездок) КАК НомерХодкиНаДату
ИЗ
ВТ_Ходки КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Ходки КАК Т2
ПО Т1.Вагон = Т2.Вагон
И Т1.Период >= Т2.Период
ГРУППИРОВАТЬ ПО
Т1.Вагон,
Т1.Период
Важный нюанс: Если в одну и ту же секунду (период) у одного вагона может быть несколько записей, условие Т1.Период >= Т2.Период выдаст неверный результат (дублирование сумм). В этом случае в соединение нужно добавить уникальный идентификатор записи или дополнительное поле (например, номер строки или более точное время).
Если мы не хотим перегружать запрос сложными соединениями, можно воспользоваться встроенными возможностями СКД. Функция ВычислитьВыражение позволяет обращаться к другим записям выборки прямо в процессе формирования отчета.
Рассмотрим подробнее синтаксис для нарастающего итога:
ВычислитьВыражение("Сумма(Количество)", , , "Первая", "Текущая")
Чтобы расчет корректно "сбрасывался" при переходе от одного вагона к другому, необходимо правильно настроить область расчета. Разберем алгоритм настройки:
НарастающийИтог).
ВычислитьВыражение("Сумма(КоличествоПогрузок)", "Вагон", "Группировка", "Первая", "Текущая")
Период или ДетальныеЗаписи). Это критически важно: если рассчитывать это выражение по группировке "Вагон", система выдаст общий итог, а не нарастающий.Проанализируем параметры функции:
"Сумма(КоличествоПогрузок)" — что мы считаем."Вагон" — группировка, в рамках которой производится расчет (как только вагон меняется, счетчик обнуляется)."Группировка" — область вычисления."Первая" и "Текущая" — границы расчета (от самой первой записи по этому вагону до строки, которая выводится сейчас).Этот метод считается наиболее гибким, когда нужно считать данные "с начала времен", а не только за период отчета. Например, отчет формируется за квартал, но нам нужно знать, сколько раз вагон ходил вообще, начиная с 2019 года.
Разберем, как это реализовать:
НаборДанных1.НаборДанных2.Вагон = Вагон.Период из второго набора должен быть меньше или равен Периоду из первого набора.В этом случае для каждой строки первого набора СКД выполнит отдельный подзапрос (или оптимизированную выборку) ко второму набору, передав туда текущую дату как фильтр "СВЕРХУ". В результате мы получим точное количество ходок на конкретный момент времени без сложных JOIN в основном запросе.
Иногда задача усложняется: нужно не просто посчитать количество, а вычислить "среднее количество погрузок в день на текущую дату". Здесь обычного суммирования недостаточно. Выясним причину: если за один день было 3 погрузки, среднее по записям и среднее по дням — это разные вещи.
Для решения таких задач в новых версиях платформы (8.3.15+) используем функцию ВычислитьВыражениеСГруппировкойМассив. Она позволяет собрать данные во временный массив в памяти СКД, а затем применить к ним итоговую функцию.
Пример выражения для среднего значения нарастающим итогом:
Среднее(ВычислитьВыражениеСГруппировкойМассив("Сумма(Количество)", "ПериодДень"))
Здесь система сначала сгруппирует данные по дням, а затем посчитает среднее от этих сумм. Это позволяет избежать искажения статистики из-за неравномерного количества записей в разные дни.
Проанализируем ситуацию, когда вагон не ходил в определенные дни, но нам нужно протащить нарастающий итог через эти даты. Если просто выбрать данные из регистра, дат, когда не было движений, в отчете не будет.
Чтобы решить эту проблему, используем механизм дополнения периодов:
Период перейдите к настройкам иерархии и выберите "Дополнять периоды".ВычислитьВыражение (Метод 2) автоматически заполнит "пустые" строки значениями из предыдущих периодов, так как для них граница "Текущая" будет включать последнее известное значение.Заключение: Для простых отчетов с небольшим объемом данных лучше использовать Метод 2 (ресурсные функции), так как это сохраняет запрос читаемым. Если же требуется высокая производительность при расчете огромных массивов данных (сотни тысяч вагонов), рекомендуется Метод 1 (запрос) с предварительной индексацией временных таблиц по полям Вагон и Период.