В практике разработки отчетов на Системе компоновки данных (СКД) часто возникает задача не просто просуммировать данные, а выполнить сложный расчет на основе уже агрегированных показателей. Типичный пример: необходимо найти месяц с максимальным расходом для каждой позиции номенклатуры. В этой статье мы подробно разберем, как правильно использовать функции СКД для получения таких результатов, проанализируем типичные ошибки и рассмотрим несколько способов реализации задачи.
Представим ситуацию: у нас есть отчет, в котором данные сгруппированы по Номенклатура, а внутри — по ПериодМесяц. Нам необходимо в строке с номенклатурой вывести значение расхода того месяца, где этот расход был максимальным. Если мы просто укажем ресурс Максимум(Расход), система может вернуть максимальную детальную запись, а не максимум из месячных итогов. Наша цель — получить именно максимум от сумм.
Для начала разберем формулу расхода, которую часто используют, если в наборе данных нет готового поля «Расход»:
(КоличествоБазовыхЕдНачальныйОстаток + КоличествоБазовыхЕдПриход) - КоличествоБазовыхЕдКонечныйОстаток
Важно помнить, что в СКД расчет ресурсов по остаткам имеет свои особенности, связанные с ролями полей (Начальный остаток, Конечный остаток). Поэтому расчет расхода лучше производить либо на уровне запроса, либо через вычисляемые поля с учетом итогов.
Это самый современный и эффективный метод решения задач «агрегат от агрегата». Рассмотрим подробнее, как работает эта функция. Она позволяет вычислить выражение для каждой строки указанной группировки, собрать результаты в массив и затем применить к этому массиву другую агрегатную функцию.
Алгоритм настройки:
МаксимальныйРасход).
Максимум(ВычислитьВыражениеСГруппировкойМассив("Сумма(Расход)", "ПериодМесяц"))
Проанализируем, что здесь происходит:
"Сумма(Расход)" — это то, что мы считаем для каждого месяца."ПериодМесяц" — это имя поля группировки, по которой собираются итоги в массив.Максимум(...) — внешняя функция, которая выбирает наибольшее число из полученного массива сумм.Важный нюанс: если вы рассчитываете расход по формуле через остатки, выражение внутри может выглядеть сложнее. Однако рекомендуется предварительно вычислить поле Расход в запросе или в вычисляемых полях, чтобы избежать путаницы с итогами.
СКД позволяет гибко настраивать расчет одного и того же ресурса для разных уровней отчета. Это полезно, если вы хотите видеть обычную сумму в строках месяцев, но максимум — в строке номенклатуры.
Разберем по шагам, как это настроить в окне ресурсов:
Расход.Сумма(Расход) и в колонке «Рассчитывать по...» выбираем ПериодМесяц.Расход. В выражении пишем нашу конструкцию с массивом:
Максимум(ВычислитьВыражениеСГруппировкойМассив("Сумма(Расход)", "ПериодМесяц"))
А в колонке «Рассчитывать по...» выбираем Номенклатура и ОбщийИтог.
Таким образом, система будет «понимать»: когда мы находимся внутри месяца — нужно просто сложить цифры. Когда мы поднимаемся на уровень выше к номенклатуре — нужно посмотреть на все итоги месяцев и выбрать самый большой.
Часто разработчики пытаются использовать функцию ВычислитьВыражение с параметрами границ окна, например "Первая", "Первая" или "Первая", "Последняя". Рассмотрим, почему это может не работать в данной задаче.
Параметры "Первая", "Первая" ограничивают область расчета только самой первой строкой текущего контекста. Если вы ищете максимум, указывая такие границы, система сравнит первую строку с самой собой и вернет её значение. Для поиска максимума по всей группировке теоретически можно использовать "Первая", "Последняя", но функция ВычислитьВыражениеСГруппировкойМассив гораздо стабильнее, так как она специально спроектирована для работы с итогами вложенных уровней.
Еще одна проблема — NULL значения. Если в каком-то месяце не было движений, формула (Начальный + Приход) - Конечный может вернуть неожиданный результат или NULL. Всегда используйте проверку:
ЕстьNULL(КоличествоБазовыхЕдРасход, 0)
Если отчет строится по огромному количеству данных и функции СКД начинают замедлять работу, можно перенести расчет максимума в текст запроса. Выясним, как это сделать правильно.
Проанализируем структуру такого запроса:
ВТ_ЕжемесячныйРасход) мы группируем данные по номенклатуре и месяцу, вычисляя сумму расхода.МАКСИМУМ к полю расхода.
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ) КАК Месяц,
СУММА(Продажи.Количество) КАК РасходМесяца
ПОМЕСТИТЬ ВТ_РасходПоМесяцам
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГРУППИРОВАТЬ ПО
Продажи.Номенклатура,
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ)
;
ВЫБРАТЬ
ВТ_РасходПоМесяцам.Номенклатура,
МАКСИМУМ(ВТ_РасходПоМесяцам.РасходМесяца) КАК МаксимальныйЕжемесячныйРасход
ИЗ
ВТ_РасходПоМесяцам КАК ВТ_РасходПоМесяцам
ГРУППИРОВАТЬ ПО
ВТ_РасходПоМесяцам.Номенклатура
Этот метод лишает нас гибкости СКД (например, если пользователь захочет сгруппировать данные не по месяцам, а по неделям, запрос придется переписывать), но он работает быстрее на больших объемах данных, так как СУБД выполняет агрегацию эффективнее, чем построчный перебор в памяти СКД.
Мы рассмотрели основной способ нахождения максимального расхода в отчете 1С через СКД. Использование функции ВычислитьВыражениеСГруппировкойМассив является наиболее предпочтительным, так как оно сохраняет все преимущества компоновки данных и позволяет корректно работать с иерархией отчета. Помните о правильной настройке ресурсов для разных уровней группировок и всегда проверяйте данные на наличие NULL.