При работе с Системой компоновки данных (СКД) в 1С разработчики часто сталкиваются с необходимостью реализовать сложную логику расчета итогов. Типичная задача — классифицировать номенклатурные позиции по определенным критериям (например, продавался товар или нет) и рассчитать общую сумму или количество таких позиций в разрезе группировок. При написании формул легко допустить как синтаксические ошибки (перепутать количество скобок или параметров функций), так и архитектурные (попытаться выполнить агрегирование там, где это недопустимо).
В этой статье мы подробно разберем, почему возникает популярная ошибка с подсчетом скобок при использовании функций СУММА() и ЕСТЬNULL(), рассмотрим разницу между вычисляемыми полями и ресурсами СКД, а также изучим лучшие практики оптимизации таких выражений для повышения производительности отчетов.
Рассмотрим выражение, которое изначально пытался использовать разработчик для вычисления продаваемых позиций:
ВЫБОР
КОГДА ЕСТЬNULL(Сумма(Ср_Остаток, 0)) < 1
И ЕСТЬNULL(Сумма(СтоимостьОборот, 0)) >= 0
ТОГДА 0
КОГДА ЕСТЬNULL(Сумма(Ср_Остаток, 0)) >= 1
И ЕСТЬNULL(Сумма(СтоимостьОборот, 0)) = 0
ТОГДА 1
КОГДА ЕСТЬNULL(Сумма((Ср_Остаток, 0)) >= 1
И ЕСТЬNULL(Сумма((СтоимостьОборот, 0)) >= 0
ТОГДА 2
КОНЕЦ
При проверке или компоновке этого выражения система выдает синтаксическую ошибку, указывающую на скобки. Проанализируем ситуацию и выясним истинные причины этой ошибки. Здесь присутствуют сразу три критические проблемы:
КОГДА перед именами полей стоят двойные открывающие скобки: Сумма((Ср_Остаток... и Сумма((СтоимостьОборот..., но закрываются они некорректно. Это классическая опечатка, которую СУБД или парсер СКД не могут обработать.СУММА(): Это главная методологическая ошибка. Разработчик написал конструкцию вида Сумма(Ср_Остаток, 0). В платформе 1С агрегатная функция СУММА() принимает на вход ровно один параметр — выражение, числовые значения которого необходимо сложить. Передача второго параметра через запятую недопустима и вызывает ошибку компиляции.ЕСТЬNULL() и СУММА(): Исходя из синтаксиса, автор выражения хотел подменить значение NULL нулем, если сумма окажется пустой. Для этого функция ЕСТЬNULL() должна обрамлять функцию СУММА(), а не находиться внутри нее в качестве второго аргумента. Правильная конструкция должна выглядеть как ЕСТЬNULL(СУММА(Поле), 0).Давайте разберем по шагам, как исправить эту формулу и как правильно спроектировать схему компоновки данных для решения подобной задачи.
Прежде чем писать формулы, важно понять, на каком этапе компоновки данных они будут рассчитываться. В СКД существует жесткое разграничение между двумя механизмами:
СУММА(), КОЛИЧЕСТВО(), СРЕДНЕЕ()). Если попытаться написать агрегатную функцию в выражении вычисляемого поля, система выдаст ошибку на этапе компоновки результата. Здесь можно использовать только логические операторы и простые функции для обработки текущей строки (например, ЕСТЬNULL(Поле, 0) или ВЫБОР... без агрегатов).Чтобы реализовать задачу автора, мы можем пойти двумя путями. Рассмотрим каждый из них подробно.
Этот способ является наиболее чистым с точки зрения архитектуры СКД. Мы разделяем логику определения статуса строки и последующее суммирование этих статусов.
Шаг 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), так и в группировках (где система корректно просуммирует эти значения).
Если нам не нужно детальное поле в строках отчета, а требуется сразу получить просуммированный итог по группировкам, мы можем написать единую формулу на вкладке «Ресурсы» для существующего поля или нового ресурса. Для этого обернем всю конструкцию ВЫБОР внутрь агрегатной функции СУММА().
Добавим в ресурсы поле и пропишем для него следующее выражение:
СУММА(ВЫБОР
КОГДА ЕСТЬNULL(Ср_Остаток, 0) < 1 И ЕСТЬNULL(СтоимостьОборот, 0) >= 0
ТОГДА 0
КОГДА ЕСТЬNULL(Ср_Остаток, 0) >= 1 И ЕСТЬNULL(СтоимостьОборот, 0) = 0
ТОГДА 1
КОГДА ЕСТЬNULL(Ср_Остаток, 0) >= 1 И ЕСТЬNULL(СтоимостьОборот, 0) > 0
ТОГДА 2
КОНЕЦ)
Проанализируем, как работает данная конструкция. При расчете итогов по группировке СКД обратится к детальным записям этой группировки. Для каждой строки будет вычислено условие ВЫБОР, которое вернет число (0, 1 или 2). После этого функция СУММА() сложит полученные результаты всех строк группировки. Это лаконичное и полностью рабочее решение, не требующее создания дополнительных вычисляемых полей.
При написании выражений в СКД и запросах 1С важно задумываться о производительности, особенно на больших базах данных. Рассмотрим две схожие конструкции:
СУММА(ЕСТЬNULL(Поле, 0))ЕСТЬNULL(СУММА(Поле), 0)Хотя на первый взгляд они кажутся идентичными, с точки зрения СУБД между ними есть огромная разница:
В первом варианте — СУММА(ЕСТЬNULL(Поле, 0)) — СУБД будет вынуждена вызывать функцию проверки значения на NULL для каждой строки таблицы еще до того, как начнется суммирование. Если в таблице миллионы записей, это приведет к серьезному замедлению выполнения отчета.
Во втором варианте — ЕСТЬNULL(СУММА(Поле), 0) — СУБД сначала быстро выполнит агрегирование (сложение) всех значений поля на уровне базы данных, игнорируя значения NULL (так как функция SUM в SQL автоматически их пропускает). Проверка ЕСТЬNULL выполнится всего один раз — для уже рассчитанного финального итога группировки. Это значительно быстрее и эффективнее.
Правило: Всегда старайтесь выносить функцию ЕСТЬNULL() наружу агрегатных функций, если только логика расчета строки не требует обязательной замены NULL на 0 до выполнения сложения с другими полями внутри строки.
Проанализируем бизнес-логику исходного запроса. В формуле фигурирует поле Ср_Остаток (средний остаток). Использование функции СУММА() для усредненных показателей (таких как средняя цена, средний остаток или наценка) — это классическая методологическая ошибка.
Средний остаток — это показатель, который уже рассчитан за определенный период времени. Если мы начнем просто суммировать средние остатки по группировкам (например, складывать средний остаток за январь, февраль и март или суммировать средние остатки по складам), мы получим бессмысленное и завышенное число, которое не отражает реальное положение дел на складе.
Для корректной работы с такими показателями в СКД следует использовать один из следующих подходов:
СРЕДНЕЕ(Ср_Остаток) на вкладке ресурсов, чтобы получать усредненное значение по группировкам.Учитывая эти архитектурные, синтаксические и методологические особенности СКД, вы сможете создавать не просто работающие отчеты, но и оптимизированные, быстродействующие решения, которые предоставляют пользователям математически корректные данные.