Как рассчитать нестандартный итог и получить разницу итогов колонок в запросе и СКД?

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

При построении отчетов в платформе 1С разработчики часто сталкиваются с задачей, когда итоговое значение по колонке должно рассчитываться не простым сложением строк, а по специальному алгоритму. Типичный пример: необходимо получить разность общих итогов двух разных показателей (например, из суммы колонки А вычесть сумму колонки Б), при условии, что в детальных записях эти данные могут находиться в разных строках или на разных уровнях группировки.

Разберем, почему стандартная секция ИТОГИ в тексте обычного запроса не позволяет решить эту задачу напрямую, какие существуют обходные пути для встроенного языка и как правильно настроить такой расчет в Системе компоновки данных (СКД).

Ограничения секции ИТОГИ в обычном запросе

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


// Ошибочная конструкция!
ИТОГИ
    СУММА(ПолеА) - СУММА(ПолеБ)
ПО
    ОБЩИЕ

Такой запрос завершится аварийно с ошибкой компиляции: «Ожидается агрегатная функция». В платформе 1С секция ИТОГИ имеет строгое ограничение: внутри нее допускается использование исключительно стандартных агрегатных функций (СУММА, КОЛИЧЕСТВО, МИНИМУМ, МАКСИМУМ, СРЕДНЕЕ). Производить арифметические действия между агрегатами в секции ИТОГИ запрещено.

Запись вида СУММА(ПолеА - ПолеБ) синтаксически корректна, однако она выполняет вычитание построчно внутри каждой физической записи выборки. Если показатели ПолеА и ПолеБ распределены по разным строкам (например, соответствуют разным видам движений или статьям), такой расчет вернет неверный результат либо значение NULL.

Способы решения задачи в обычном запросе

Рассмотрим практические варианты обхода этого ограничения без использования компоновки данных.

  1. Формирование знакового поля в блоке ВЫБРАТЬ

    Если показатели разнесены по разным записям, мы можем привести их к общему результирующему показателю с учетом знака еще на этапе выборки строк:

    
    ВЫБРАТЬ
        Ссылка,
        ВЫБОР
            КОГДА ТипДвижения = ЗНАЧЕНИЕ(Перечисление.Типы.Приход) ТОГДА Сумма
            КОГДА ТипДвижения = ЗНАЧЕНИЕ(Перечисление.Типы.Расход) ТОГДА -Сумма
            ИНАЧЕ 0
        КОНЕЦ КАК РазницаПоказателей
    ИЗ
        РегистрНакопления.Движения
    ИТОГИ
        СУММА(РазницаПоказателей)
    ПО
        ОБЩИЕ
    

    В этом случае в секции ИТОГИ применяется стандартная агрегатная функция СУММА, суммирующая положительные и отрицательные величины, что дает правильную разницу итогов.

  2. Предварительная группировка и соединение во временных таблицах

    Если данные берутся из разных источников или требуют сложной агрегации, свернем их в пакете запросов в отдельные временные таблицы, а затем соединим по ключевым разрезам. Когда показатели ПолеА и ПолеБ окажутся в одной результирующей строке на одном уровне группировки, их можно вычитать стандартным образом.

  3. Расчет разности средствами встроенного языка 1С

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

    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ПолеА,
        |   ПолеБ
        |ИЗ
        |   Документ.РеализацияТоваровУслуг
        |ИТОГИ
        |   СУММА(ПолеА),
        |   СУММА(ПолеБ)
        |ПО
        |   ОБЩИЕ";
    
    Результат = Запрос.Выполнить();
    ВыборкаИтоги = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    
    Если ВыборкаИтоги.Следующий() Тогда
        ИтоговаяРазница = ВыборкаИтоги.ПолеА - ВыборкаИтоги.ПолеБ;
    КонецЕсли;
    

Реализация расчета разницы итогов в СКД

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

Частая ошибка разработчиков заключается в попытке создать вычисляемое поле с выражением ПолеА - ПолеБ. В этом случае вычитание происходит на уровне детальных записей до агрегации, что не решает проблему, если значения находятся в разных строках отчета.

Правильный алгоритм действий выглядит следующим образом:

  1. Настройка закладки «Ресурсы»

    Перейдем на вкладку Ресурсы схемы компоновки данных. Добавим целевое поле (это может быть новое поле или одно из существующих) и пропишем итоговое выражение с применением агрегатных функций непосредственно в колонке Выражение:

    СУММА(ПолеА) - СУММА(ПолеБ)

    В отличие от обычного запроса, СКД сначала рассчитает агрегаты СУММА(ПолеА) и СУММА(ПолеБ) для текущей группировки или общего итога, а затем вычислит их разность.

  2. Защита от значений NULL

    Если в какой-либо группировке одно из полей окажется незаполненным, результатом операции вычитания станет значение NULL. Чтобы избежать потери данных, обернем поля в функцию ЕстьNULL:

    ЕстьNULL(СУММА(ПолеА), 0) - ЕстьNULL(СУММА(ПолеБ), 0)
  3. Раздельный расчет по группировкам («Рассчитывать по...»)

    Если для детальных записей вывод разности не требуется, а расчет необходим только для промежуточных группировок и общих итогов отчета, используем колонку Рассчитывать по...:

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

  4. Использование контекстной функции ВычислитьВыражение

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

    СУММА(ПолеА) - ВычислитьВыражение("СУММА(ПолеБ)", "ОбщийИтог")

    Второй параметр функции задает контекст вычисления, что дает возможность сопоставлять данные разных иерархических уровней отчета в одном выражении ресурса.

← На главную