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