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