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