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