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