При разработке отчетов на базе системы компоновки данных (СКД) часто возникает задача вывести порядковый номер строки и в подвале отчета получить сумму этих номеров. На первый взгляд решение кажется очевидным: добавить вычисляемое поле для нумерации и вынести его в ресурсы с агрегатной функцией Сумма. Однако на практике разработчики сталкиваются с тем, что вместо ожидаемой суммы чисел от 1 до N система возвращает совершенно иное, завышенное значение. Разберем эту ситуацию, выясним причину такого поведения платформы и подробно рассмотрим варианты получения правильного результата.
Проанализируем типовую ситуацию. Предположим, у нас есть набор данных с 12 записями. Для нумерации строк создается вычисляемое поле НомерСтроки со следующим выражением нарастающего итога:
ВычислитьВыражение("Сумма(1)", , , "Первая", "Текущая")
На уровне детальных записей такое выражение работает отлично: в каждой строке выводится ее порядковый номер от 1 до 12. Проблема появляется при попытке просуммировать это поле в ресурсах стандартной конструкцией Сумма(НомерСтроки). В итоге вместо ожидаемого значения 78 (так как 1 + 2 + ... + 12 = 78) отчет выводит 144.
Выясним причину такого поведения СКД. Вычисляемые поля рассчитываются в контексте той группировки, для которой они запрашиваются. Когда СКД начинает рассчитывать общий итог для ресурса, выражение поля вычисляется в контексте всей выборки данных. Для общего итога отчета «текущая» запись совпадает с «последней», а нарастающий итог Сумма(1) по всей таблице равен 12. После этого СКД применяет агрегатную функцию Сумма по детальным записям: берет значение 12 и складывает его 12 раз (12 * 12), что и дает в результате 144.
Стандартные системные поля (например, СистемныеПоля.НомерПоПорядку) также не решают проблему, поскольку они служат исключительно для оформления вывода и не могут быть агрегированы как полноценные числовые ресурсы. Рассмотрим рабочие способы решения этой задачи.
Рассмотрим самый быстрый, изящный и не требующий перестроения запросов способ. Последовательность номеров строк от 1 до N представляет собой классическую арифметическую прогрессию. Сумма первых N натуральных чисел рассчитывается по математической формуле:
S = N * (N + 1) / 2
Так как общее количество строк в группировке можно легко получить стандартными функциями СКД, применим эту формулу непосредственно на вкладке Ресурсы.
Разберем по шагам настройку схемы компоновки данных:
НомерСтроки с выражением:
ВычислитьВыражение("Сумма(1)", , , "Первая", "Текущая")НомерСтроки дважды.НомерСтрокиКоличество() * (Количество() + 1) / 2
Сумма(1) * (Сумма(1) + 1) / 2Посмотрим на результат: в детальных записях выводятся значения от 1 до 12, а в строке общего итога СКД производит расчет: 12 * (12 + 1) / 2 = 78. Задача решена без перегрузки процессора компоновки.
Рассмотрим подробнее альтернативный способ в СКД, который позволяет собрать реальные значения детальных записей в коллекцию и просуммировать их на верхнем уровне. Для этого используется специализированная агрегатная функция языка выражений СКД — ВычислитьВыражениеСГруппировкойМассив.
Разберем настройку вкладки Ресурсы:
НомерСтроки при расчете по группировке детальных записей оставляем значение:
НомерСтрокиСумма(ВычислитьВыражениеСГруппировкойМассив("НомерСтроки", "Числа"))
"Числа" — имя поля группировки детальных записей отчета).Проанализируем работу этой конструкции: функция собирает массив вычисленных номеров строк [1, 2, 3, ..., 12], после чего внешняя функция Сумма складывает элементы полученного массива, возвращая искомое число 78.
Если нумерация требуется не только для визуализации, но и должна участвовать в сложных кросс-табличных расчетах, целесообразно сформировать номер строки заранее в тексте запроса набора данных. В таком случае номер становится обычным числовым полем выборки, и к нему можно применять стандартную агрегацию Сумма(НомерСтроки) без дополнительных условий.
Посмотрим на пример формирования номеров строк в тексте запроса с помощью соединения таблицы самой с собой:
ВЫБРАТЬ
ИсходныеДанные.Число КАК Число
ПОМЕСТИТЬ ВТ_Данные
ИЗ
&ТаблицаЧисел КАК ИсходныеДанные
;
ВЫБРАТЬ
Т1.Число КАК Число,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т2.Число) КАК НомерСтроки
ИЗ
ВТ_Данные КАК Т1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
ПО Т1.Число >= Т2.Число
СГРУППИРОВАТЬ ПО
Т1.Число
При таком подходе поле НомерСтроки является полноценным числовым реквизитом источника. В настройках СКД достаточно указать стандартное выражение ресурса Сумма(НомерСтроки), и итоговые суммы посчитаются корректно на всех уровнях отчета.