При разработке сложных отчетов в Системе Компоновки Данных (СКД) часто возникает задача: в рамках одного отчета нужно вывести несколько блоков данных из одного и того же источника (например, регистра), но с разными, порой взаимоисключающими, отборами. Кроме того, может потребоваться рассчитать производные показатели, такие как скользящее среднее за 3 или 6 месяцев.
Первая мысль, которая приходит в голову — создать несколько запросов с нужными отборами во временные таблицы (ВТ) и затем объединить их. Этот подход рабочий, но не всегда оптимальный. СКД предоставляет более гибкие и производительные инструменты для решения таких задач. Рассмотрим их подробно.
Это самый логичный и "правильный" способ разделения данных с разными отборами в СКД. Идея заключается в том, чтобы для каждой группы отборов создать свой собственный набор данных, а затем связать их для совместного вывода в отчете.
Разберем по шагам:
"Запрос", назовем его ДанныеСОтбором1. В тексте запроса получите необходимые поля из регистра и примените первый набор жестких отборов в условии ГДЕ. Сгруппируйте данные по месяцам.
// Примерный текст запроса для НабораДанных1
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ) КАК Месяц,
СУММА(Продажи.Сумма) КАК СуммаПродаж
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Номенклатура.ВидНоменклатуры = &ВидНоменклатуры1
И Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ)
ДанныеСОтбором2, но в условии ГДЕ используйте уже второй набор отборов.
// Примерный текст запроса для НабораДанных2
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ) КАК Месяц,
СУММА(Продажи.Сумма) КАК СуммаПродаж
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
Продажи.Контрагент.Регион = &Регион2
И Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ)
Источник, другой — как Приемник. В качестве условия связи выберите общее поле, в нашем случае это Месяц. Выражение источника: Месяц, Выражение приемника: Месяц.После этого на вкладке "Настройки" вы сможете в одной таблице или в разных группировках выводить поля из обоих наборов данных (например, СуммаПродаж из первого и СуммаПродаж из второго), и СКД корректно сопоставит их по месяцам.
Преимущества:
Этот метод предполагает, что мы получаем все данные одним запросом, без жесткой фильтрации, а разделение на группы производим уже средствами СКД.
Рассмотрим два варианта реализации этого подхода.
Этот способ идеально подходит, когда нужно в одной строке отчета показать разные показатели, рассчитанные по разным условиям.
СуммаПоОтбору1. В колонке "Выражение" напишите формулу с использованием функции языка выражений СКД Выбор().
Выбор
Когда Номенклатура.ВидНоменклатуры = &ВидНоменклатуры1
Тогда Сумма
Иначе 0
Конец
СуммаПоОтбору2 с другим условием.
Выбор
Когда Контрагент.Регион = &Регион2
Тогда Сумма
Иначе 0
Конец
СуммаПоОтбору1 и СуммаПоОтбору2 нужно рассчитывать Сумму().В настройках отчета вы сможете вывести эти два ресурса как отдельные колонки. СКД при обходе записей будет проверять условие и помещать значение в нужный "контейнер" для последующего суммирования.
Можно добавить поле-признак прямо в запрос с помощью конструкции ВЫБОР КОГДА.
ВЫБРАТЬ
Продажи.Период,
Продажи.Сумма,
ВЫБОР
КОГДА Продажи.Номенклатура.ВидНоменклатуры = &ВидНоменклатуры1
ТОГДА "Группа отбора 1"
КОГДА Продажи.Контрагент.Регион = &Регион2
ТОГДА "Группа отбора 2"
ИНАЧЕ "Прочее"
КОНЕЦ КАК ГруппаОтбора
ИЗ
РегистрНакопления.Продажи КАК Продажи
ГДЕ
// Общие отборы, не взаимоисключающие
Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
В результате у вас появится новое поле ГруппаОтбора. В настройках отчета вы можете использовать это поле как группировку строк или колонок, чтобы разделить данные на нужные блоки.
Расчет скользящих средних (например, среднее за последние 3 месяца) — более сложная задача для СКД, так как стандартные агрегатные функции (Сумма, Среднее) работают только в пределах текущей группировки.
Самый надежный способ — рассчитать эти показатели на уровне запроса.
Если ваша СУБД (например, MS SQL Server или PostgreSQL) поддерживает оконные функции, это наиболее производительный и элегантный вариант.
// Пример для СУБД с поддержкой оконных функций
ВЫБРАТЬ
Данные.Месяц,
Данные.СуммаПродаж,
AVG(Данные.СуммаПродаж) OVER (ORDER BY Данные.Месяц ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) КАК СреднееЗа3Месяца,
AVG(Данные.СуммаПродаж) OVER (ORDER BY Данные.Месяц ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) КАК СреднееЗа6Месяцев
ИЗ
(ВЫБРАТЬ
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ) КАК Месяц,
СУММА(Продажи.Сумма) КАК СуммаПродаж
ИЗ
РегистрНакопления.Продажи КАК Продажи
// ...Условия отбора...
СГРУППИРОВАТЬ ПО
НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ)
) КАК Данные
Если оконные функции недоступны, можно использовать соединение таблицы с самой собой (self-join), но этот способ сложнее в написании и может быть менее производительным.
Пытаться рассчитать скользящее среднее в вычисляемых полях или ресурсах СКД с помощью функций вроде ВычислитьВыражение() не рекомендуется. Это приводит к очень сложным конструкциям и крайне низкой производительности отчета.
Для решения исходной задачи автора темы оптимальным будет комбинированный подход:
ДанныеСОтбором1 и ДанныеСОтбором2), как описано в Решении 1. Это обеспечит логическое разделение данных.Месяц."Таблица".
ДанныеСОтбором1 (Месяц, СуммаПродаж, СреднееЗа3Месяца и т.д.).ДанныеСОтбором2.Такая структура позволит получить четкий, производительный и легко поддерживаемый отчет, полностью отвечающий поставленным требованиям, без "хитростей" и избыточного усложнения.