Как рассчитать разницу между значениями показателей в соседних колонках месяцев в отчете СКД?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При построении отчетов в виде кросс-таблицы в системе компоновки данных (СКД) часто возникает задача сравнения периодов: например, вывести помесячные колонки и рядом или вместо абсолютных значений отобразить динамику — отклонение текущего месяца от предыдущего. Рассмотрим, почему типовые попытки простого вычитания вызывают сложности, разберем работу встроенной функции СКД ВычислитьВыражение и изучим альтернативный вариант расчета в запросе.

Анализ проблемы и подводные камни группировки

Разберем типичные ошибки, с которыми сталкиваются разработчики при попытке посчитать помесячную разницу:

  1. Группировка по функции МЕСЯЦ(Дата): функция языка запросов МЕСЯЦ() возвращает порядковый номер месяца от 1 до 12. Если отчет строится за период, превышающий один календарный год, либо охватывает переход через границу года (например, с ноября по февраль), данные за октябрь прошлого и текущего года схлопнутся в одну колонку. Для корректной шкалы времени всегда используем группировку НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ).
  2. Обработка значений NULL: для самого первого месяца в отчете физически нет предыдущего периода. Любое вычитание вида Число - NULL в платформе 1С дает результат NULL. В итоге колонка за первый месяц полностью очищается, если не предусмотреть обработку пустых значений.
  3. Пропуски данных: стандартный переход к предыдущей строке/колонке опирается на физически существующие элементы. Если за какой-то месяц движений не было, предыдущим станет более ранний месяц, и разница будет рассчитана не за один шаг, а за несколько.
  4. Агрегатные функции: при передаче имени ресурса в функцию ВычислитьВыражение обязательно требуется указывать агрегатную функцию (например, Сумма), иначе система компоновки данных не сможет рассчитать значение на уровне группировок.

Способ 1. Использование функции ВычислитьВыражение в ресурсах СКД

Рассмотрим, как настроить расчет непосредственно в СКД без переписывания исходного запроса. Перейдем на вкладку Ресурсы в схеме компоновки данных.

Выясним причину, почему выражение вида ВычислитьВыражение("Показатель1",,,"Предыдущая","Предыдущая") выдает ошибку или некорректный результат. Для кросс-таблицы необходимо явно передать контекст группировки и обернуть показатель в агрегатную функцию, а также защитить расчет от NULL с помощью ЕстьNull().

Разберем рабочий пример формулы для ресурса Показатель1Отклонение:


Сумма(Показатель1) - ЕстьNull(ВычислитьВыражение("Сумма(Показатель1)", "ПериодМесяц", , "Предыдущая", "Предыдущая"), 0)

Проанализируем параметры, переданные в функцию ВычислитьВыражение:

  1. Выражение: "Сумма(Показатель1)" — строка с агрегатным выражением, значение которого вычисляется в указанном контексте.
  2. Группировка: "ПериодМесяц" — имя группировки колонок, по которой производится навигация назад.
  3. Область: оставлена пустой, расчет идет в рамках текущей группировки.
  4. Начальная позиция и конечная позиция: переданы как "Предыдущая", "Предыдущая", что предписывает платформе взять ровно одну предыдущую запись в данной группировке.

Важное замечание: функция привязана к экранному порядку колонок. Если пользователь изменит сортировку колонок на убывающую, расчет начнет вычитать значения будущих периодов. Кроме того, в строке «Общий итог» обращение к предыдущей записи теряет смысл, поэтому для общего итога в настройках ресурса («Рассчитывать по…») следует задать отдельное выражение.

Способ 2. Расчет отклонений на уровне запроса (левое соединение)

Посмотрим на более надежный и контролируемый подход: получение данных за предыдущий месяц прямо в тексте запроса. Это устраняет зависимость от порядка сортировки на форме отчета и гарантирует жесткую календарную привязку.

Разберем реализацию с помощью временных таблиц и левого соединения со сдвигом даты:


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ) КАК ПериодМесяц,
    Продажи.Номенклатура КАК Номенклатура,
    СУММА(Продажи.Сумма) КАК Сумма
ПОМЕСТИТЬ ВТ_ДанныеПоМесяцам
ИЗ
    РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
    НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ),
    Продажи.Номенклатура
;

ВЫБРАТЬ
    Текущие.ПериодМесяц КАК ПериодМесяц,
    Текущие.Номенклатура КАК Номенклатура,
    Текущие.Сумма КАК СуммаТекущая,
    ЕСТЬNULL(Прошлые.Сумма, 0) КАК СуммаПрошлогоМесяца,
    Текущие.Сумма - ЕСТЬNULL(Прошлые.Сумма, 0) КАК РазницаСПрошлымМесяцем
ИЗ
    ВТ_ДанныеПоМесяцам КАК Текущие
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДанныеПоМесяцам КАК Прошлые
        ПО Текущие.Номенклатура = Прошлые.Номенклатура
        И Текущие.ПериодМесяц = ДОБАВИТЬКДАТЕ(Прошлые.ПериодМесяц, МЕСЯЦ, 1)

Проанализируем логику этого решения:

  1. В первой временной таблице ВТ_ДанныеПоМесяцам агрегируем все показатели строго с шагом НАЧАЛОПЕРИОДА(..., МЕСЯЦ).
  2. Во втором запросе соединяем таблицу саму с собой по условию Текущие.ПериодМесяц = ДОБАВИТЬКДАТЕ(Прошлые.ПериодМесяц, МЕСЯЦ, 1). Таким образом к каждой записи текущего месяца подтягиваются строго данные календарного месяца, предшествующего ему ровно на один шаг.
  3. Если за предыдущий месяц движений не было, функция ЕСТЬNULL подставит ноль, и расчет останется математически корректным без ложных сопоставлений с данными за полугодовую давность.

Способ 3. Расчет через объединение со сдвигом периода

Рассмотрим еще один эффективный прием в запросе — объединение выборок со смещением даты вперед на один месяц.


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ) КАК ПериодМесяц,
    Продажи.Номенклатура КАК Номенклатура,
    Продажи.Сумма КАК СуммаТекущая,
    0 КАК СуммаПрошлогоМесяца
ИЗ
    РегистрНакопления.Продажи КАК Продажи

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(НАЧАЛОПЕРИОДА(Продажи.Период, МЕСЯЦ), МЕСЯЦ, 1),
    Продажи.Номенклатура,
    0,
    Продажи.Сумма
ИЗ
    РегистрНакопления.Продажи КАК Продажи

При таком подходе данные прошлого периода искусственно смещаются в следующий месяц. В СКД поля СуммаТекущая и СуммаПрошлогоМесяца объявляются стандартными ресурсами с агрегатом Сумма(), а вычисляемое поле разницы формируется простой формулой СуммаТекущая - СуммаПрошлогоМесяца. Это избавляет от необходимости использовать ресурсоемкие соединения в базе данных и исключает сложные конструкции в выражениях компоновки.

← На главную