Как правильно рассчитать и обнулить ресурсы в СКД при использовании группировок и условий?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

Разработка сложных аналитических отчетов в Системе компоновки данных (СКД) часто сталкивается с необходимостью не просто суммировать показатели по детальным записям, а производить условный расчет на уровне определенных группировок. Типичный пример — необходимость обнулить показатели по сотруднику (физическому лицу), если по нему не было отработано часов или количество занимаемых ставок (должностей) равно нулю, и при этом корректно свернуть (просуммировать) эти данные на уровне вышестоящих группировок и общих итогов.

В этой статье мы подробно разберем, почему стандартные механизмы СКД при наивном подходе выдают некорректные результаты (или так называемую «ерунду» в итогах), как решить задачу с помощью мощной функции ВычислитьВыражениеСГруппировкойМассив и какие профессиональные архитектурные паттерны помогут сделать ваш отчет чистым, производительным и легко сопровождаемым.

В чем суть проблемы и почему «простые» формулы не работают?

Рассмотрим классическую ситуацию. В запросе схемы компоновки данных объединяются сведения о начислениях сотрудников и фактически отработанном времени. Нам требуется рассчитать количество занимаемых должностей (переменная Колдолж) и сумму начислений. При этом необходимо выполнить жесткое условие: если на уровне группировки по физическому лицу показатель отработанного времени или количество должностей равен нулю, все записи по данному физическому лицу должны быть исключены или обнулены как в детальных записях, так и при расчете общих итогов.

Если мы попытаемся написать в ресурсах простую формулу с использованием функции ВычислитьВыражение с направлением обхода «Иерархия», например:


Выбор 
    Когда ВычислитьВыражение("Колдолж", , "Иерархия") > 0 
        Тогда Сумма(Результат) 
    Иначе 0 
Конец

Мы столкнемся с тем, что расчет итогов сломается. Дело в том, что СКД рассчитывает ресурсы снизу вверх. Пытаясь получить значение вышестоящей или детальной записи «на лету» без жесткой привязки к контексту группировки, мы нарушаем естественный цикл компоновки. СКД не может сопоставить детальные записи нижнего уровня с агрегированными условиями верхнего уровня, что приводит к хаотичному распределению сумм и неверным общим итогам.

Решение 1. Использование встроенных параметров функции ВычислитьВыражениеСГруппировкойМассив

Самым надежным и архитектурно правильным решением в СКД является использование специализированной функции ВычислитьВыражениеСГруппировкойМассив. Она позволяет свернуть детальные записи по определенной группировке в массив прямо в оперативной памяти, применить к ним правила фильтрации и затем агрегировать (например, просуммировать).

Давайте подробно проанализируем полный синтаксис этой функции:

ВычислитьВыражениеСГруппировкойМассив(Выражение, ВыраженияПолейГруппировки, ОтборЗаписей, ОтборГруппировок)

Большинство разработчиков используют только первые два параметра, однако именно третий и четвертый параметры содержат ключ к решению нашей задачи:

  1. Выражение — выражение, которое необходимо вычислить (например, "Сумма(Результат)").
  2. ВыраженияПолейГруппировки — имя группировки, по которой собирается массив (в нашем случае "ФизЛицо").
  3. ОтборЗаписей — фильтр, накладываемый на детальные записи исходной выборки еще до выполнения каких-либо группировок.
  4. ОтборГруппировок — фильтр, который накладывается на уже сгруппированные данные. Это прямой аналог предложения HAVING в языке SQL. Именно сюда нужно передавать условия по агрегатным функциям группировки.

Давайте посмотрим на пример готового выражения для ресурса, которое решает задачу автора темы без создания лишних промежуточных полей:


Сумма(ВычислитьВыражениеСГруппировкойМассив("Сумма(Результат)", "ФизЛицо", , "Сумма(Колдолж) > 0 И Сумма(ОтработаноЧасов) > 0"))

Что здесь происходит? Разберем по шагам:

  1. СКД обращается к детальным записям в контексте группировки ФизЛицо.
  2. Для каждого физического лица вычисляются суммы показателей Колдолж и ОтработаноЧасов.
  3. Четвертый параметр (отбор группировок) проверяет: если сумма должностей больше нуля и сумма отработанных часов больше нуля, то значение Сумма(Результат) попадает в результирующий массив. Если условие не выполняется, значение игнорируется (массив для этого физлица не заполняется, то есть фактически приравнивается к нулю).
  4. Внешняя функция Сумма(...) берет полученный массив очищенных данных по всем физическим лицам и складывает их между собой. Это гарантирует абсолютно точный расчет итогов на любом уровне иерархии!

Решение 2. Тонкая настройка применения группового отбора после расчета итогов

Еще одна частая проблема, упомянутая в обсуждении: «с отборами всё плохо». Действительно, если наложить стандартный отбор в СКД на ресурс, система попытается добавить его в секцию ГДЕ исходного SQL-запроса к базе данных. На этом этапе итоговые ресурсы группировок еще не рассчитаны, поэтому отбор либо не сработает, либо исказит базовые данные отчета.

Чтобы решить эту проблему элегантно, воспользуемся возможностями настройки отборов компоновки данных:

  1. Откроем вкладку Настройки в схеме компоновки данных.
  2. Выберем конкретную группировку (например, ФизЛицо) или перейдем к общим настройкам отчета.
  3. Перейдем на вкладку Отбор.
  4. Добавим наше условие: Колдолж Больше 0.
  5. Важный шаг: обратите внимание на свойство отбора Применение (в некоторых версиях интерфейса доступно через свойства элемента отбора). По умолчанию там установлено значение Авто. Изменим его на После группировки или После группировки или для иерархии.

При такой настройке СКД сначала полностью выполнит запрос, рассчитает все итоги, распределит результаты по группировкам, и только в самый последний момент, перед выводом пользователю, «отсечет» строки, не удовлетворяющие условию. При этом итоговые суммы вышестоящих группировок пересчитаются корректно.

Решение 3. Паттерн «Вычисляемое поле с пустым выражением»

Иногда при сложных расчетах формулы в ресурсах становятся перегруженными и трудночитаемыми. Чтобы упростить схему и избежать появления промежуточных («колхозных», как выразился автор темы) ресурсов вроде РезультатДОП, применим профессиональный паттерн разработки:

  1. Перейдем на вкладку Вычисляемые поля в СКД.
  2. Добавим новое поле с именем ИтоговыйРезультат.
  3. Поле Выражение оставим абсолютно пустым. Это ключевой момент! На уровне детальных записей данное поле не будет содержать никаких значений и не будет тратить ресурсы процессора на расчеты по каждой строке сотрудника.
  4. Перейдем на вкладку Ресурсы.
  5. Добавим поле ИтоговыйРезультат в список ресурсов.
  6. В поле выражения ресурса напишем нашу формулу с фильтрацией:

Сумма(ВычислитьВыражениеСГруппировкойМассив("Сумма(Результат)", "ФизЛицо", , "Сумма(Колдолж) > 0 И Сумма(ОтработаноЧасов) > 0"))

В поле Рассчитывать по... укажем только те группировки, где этот итог нам действительно нужен (например, Организация, Подразделение и Общий итог). Для детальной группировки по сотрудникам вывод ресурса можно отключить или возвращать там базовый показатель. Такой подход разделяет логику хранения данных и логику их агрегации, делая схему чистой.

Решение 4. Использование дублирующих полей для обхода проблем с иерархией

В сообщении 9 один из участников упомянул проблему вывода физических лиц в иерархических структурах. Когда в СКД используется иерархия по какому-либо полю, функции семейства ВычислитьВыражение... могут удваивать итоги. Это происходит потому, что система начинает обрабатывать одну и ту же запись и как самостоятельный элемент, и как часть родительской группы внутри одного контекста вычислений.

Если вы столкнулись с подобным поведением при иерархических группировках, примените следующий обходной путь:

  1. В запросе схемы компоновки данных продублируйте ключевое поле. Например, выберите ФизЛицо как основное поле группировки и ФизЛицо как дополнительное поле с псевдонимом ФизЛицоДляРасчета:
    
    ВЫБРАТЬ
        ОсновныеНачисления.ФизЛицо КАК ФизЛицо,
        ОсновныеНачисления.ФизЛицо КАК ФизЛицоДляРасчета,
        ...
    
  2. В настройках ресурсов СКД укажите расчет агрегатных функций не по иерархическому полю ФизЛицо, а именно по плоскому вспомогательному полю ФизЛицоДляРасчета.
  3. В структуре отчета выводите пользователю красивое поле с иерархией, а внутреннюю математику переложите на скрытое служебное поле. Это полностью исключит дублирование показателей на разных уровнях подчинения.

Сравнительный анализ методов расчета ресурсов

Давайте сопоставим все рассмотренные методы, чтобы вы могли выбрать наиболее подходящий под архитектуру вашего отчета:

  1. ВычислитьВыражениеСГруппировкойМассив с отбором группировок:
    • Плюсы: Высокая скорость работы, вся логика инкапсулирована в одной формуле ресурса, не требует изменения структуры запроса.
    • Минусы: Требует аккуратности при написании синтаксиса параметров функции.
  2. Применение отбора «После группировки»:
    • Плюсы: Настраивается декларативно в пользовательском интерфейсе или конфигураторе без написания кода, легко модифицируется.
    • Минусы: Если требуется сложная математическая зависимость между разными ресурсами, стандартного функционала условий может не хватить.
  3. Вычисляемое поле с пустым выражением:
    • Плюсы: Идеально чистая архитектура, отсутствие мусорных данных на уровне детальных записей, максимальная гибкость.
    • Минусы: Требует добавления дополнительного элемента на вкладку вычисляемых полей.

Применяя данные подходы, вы сможете отказаться от сложных, ненадежных «колхозных» формул и условного оформления в пользу красивых, стабильных и быстродействующих решений на базе стандартных возможностей Системы компоновки данных 1С.

← На главную