Как правильно рассчитать сумму с условием в СКД и избежать ошибок в выражениях

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

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

В этой статье мы подробно разберем, почему возникает популярная ошибка с подсчетом скобок при использовании функций СУММА() и ЕСТЬNULL(), рассмотрим разницу между вычисляемыми полями и ресурсами СКД, а также изучим лучшие практики оптимизации таких выражений для повышения производительности отчетов.

Анализ исходной проблемы и разбор синтаксических ошибок

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


ВЫБОР
    КОГДА ЕСТЬNULL(Сумма(Ср_Остаток, 0)) < 1
            И ЕСТЬNULL(Сумма(СтоимостьОборот, 0)) >= 0
        ТОГДА 0
    КОГДА ЕСТЬNULL(Сумма(Ср_Остаток, 0)) >= 1
            И ЕСТЬNULL(Сумма(СтоимостьОборот, 0)) = 0
        ТОГДА 1
    КОГДА ЕСТЬNULL(Сумма((Ср_Остаток, 0)) >= 1
            И ЕСТЬNULL(Сумма((СтоимостьОборот, 0)) >= 0
        ТОГДА 2
КОНЕЦ

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

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

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

Архитектурное разделение: Вычисляемые поля против Ресурсов СКД

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

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

Решение 1. Разделение логики на Вычисляемое поле и Ресурс (Рекомендуемый подход)

Этот способ является наиболее чистым с точки зрения архитектуры СКД. Мы разделяем логику определения статуса строки и последующее суммирование этих статусов.

Шаг 1. Создание вычисляемого поля.

Перейдем на вкладку «Вычисляемые поля» в конструкторе схемы компоновки данных. Добавим новое поле со следующими параметрами:


ВЫБОР
    КОГДА ЕСТЬNULL(Ср_Остаток, 0) < 1 И ЕСТЬNULL(СтоимостьОборот, 0) >= 0
        ТОГДА 0
    КОГДА ЕСТЬNULL(Ср_Остаток, 0) >= 1 И ЕСТЬNULL(СтоимостьОборот, 0) = 0
        ТОГДА 1
    КОГДА ЕСТЬNULL(Ср_Остаток, 0) >= 1 И ЕСТЬNULL(СтоимостьОборот, 0) > 0
        ТОГДА 2
КОНЕЦ

Шаг 2. Настройка ресурса.

Теперь перейдем на вкладку «Ресурсы». Добавим наше вычисляемое поле ПозицияПродавалась в список ресурсов. В колонке «Выражение» укажем простую агрегатную функцию:

СУММА(ПозицияПродавалась)

Этот подход очень удобен тем, что мы можем легко использовать поле ПозицияПродавалась как в детальных записях (где оно будет принимать значения 0, 1 или 2), так и в группировках (где система корректно просуммирует эти значения).

Решение 2. Использование агрегирования ВЫБОР непосредственно в Ресурсах

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

Добавим в ресурсы поле и пропишем для него следующее выражение:


СУММА(ВЫБОР
    КОГДА ЕСТЬNULL(Ср_Остаток, 0) < 1 И ЕСТЬNULL(СтоимостьОборот, 0) >= 0
        ТОГДА 0
    КОГДА ЕСТЬNULL(Ср_Остаток, 0) >= 1 И ЕСТЬNULL(СтоимостьОборот, 0) = 0
        ТОГДА 1
    КОГДА ЕСТЬNULL(Ср_Остаток, 0) >= 1 И ЕСТЬNULL(СтоимостьОборот, 0) > 0
        ТОГДА 2
КОНЕЦ)

Проанализируем, как работает данная конструкция. При расчете итогов по группировке СКД обратится к детальным записям этой группировки. Для каждой строки будет вычислено условие ВЫБОР, которое вернет число (0, 1 или 2). После этого функция СУММА() сложит полученные результаты всех строк группировки. Это лаконичное и полностью рабочее решение, не требующее создания дополнительных вычисляемых полей.

Важный нюанс оптимизации: СУММА(ЕСТЬNULL(...)) против ЕСТЬNULL(СУММА(...))

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

  1. СУММА(ЕСТЬNULL(Поле, 0))
  2. ЕСТЬNULL(СУММА(Поле), 0)

Хотя на первый взгляд они кажутся идентичными, с точки зрения СУБД между ними есть огромная разница:

В первом варианте — СУММА(ЕСТЬNULL(Поле, 0)) — СУБД будет вынуждена вызывать функцию проверки значения на NULL для каждой строки таблицы еще до того, как начнется суммирование. Если в таблице миллионы записей, это приведет к серьезному замедлению выполнения отчета.

Во втором варианте — ЕСТЬNULL(СУММА(Поле), 0) — СУБД сначала быстро выполнит агрегирование (сложение) всех значений поля на уровне базы данных, игнорируя значения NULL (так как функция SUM в SQL автоматически их пропускает). Проверка ЕСТЬNULL выполнится всего один раз — для уже рассчитанного финального итога группировки. Это значительно быстрее и эффективнее.

Правило: Всегда старайтесь выносить функцию ЕСТЬNULL() наружу агрегатных функций, если только логика расчета строки не требует обязательной замены NULL на 0 до выполнения сложения с другими полями внутри строки.

Методологическая ошибка при работе со средним остатком

Проанализируем бизнес-логику исходного запроса. В формуле фигурирует поле Ср_Остаток (средний остаток). Использование функции СУММА() для усредненных показателей (таких как средняя цена, средний остаток или наценка) — это классическая методологическая ошибка.

Средний остаток — это показатель, который уже рассчитан за определенный период времени. Если мы начнем просто суммировать средние остатки по группировкам (например, складывать средний остаток за январь, февраль и март или суммировать средние остатки по складам), мы получим бессмысленное и завышенное число, которое не отражает реальное положение дел на складе.

Для корректной работы с такими показателями в СКД следует использовать один из следующих подходов:

Учитывая эти архитектурные, синтаксические и методологические особенности СКД, вы сможете создавать не просто работающие отчеты, но и оптимизированные, быстродействующие решения, которые предоставляют пользователям математически корректные данные.

← На главную