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