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