Почему не работает конструкция ВЫБОР с математическими формулами в запросе 1С?

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

При написании сложных запросов в платформе 1С разработчики часто сталкиваются с ситуацией, когда конструкция ВЫБОР КОГДА ... КОНЕЦ вместе с математическими вычислениями возвращает нулевой результат, значение NULL или уходит не в ту ветку условия. Проанализируем типичные ошибки, из-за которых формулы в запросе отказываются работать, и разберем способы их устранения.

Причины некорректной работы вычислений в запросе

Разберем основные факторы, влияющие на расчет результатов в языке запросов 1С:

  1. Распространение NULL в математических выражениях. В языке запросов 1С любая арифметическая операция (сложение, вычитание, умножение, деление) с полем, значение которого равно NULL, дает в результате NULL. Общая обертка вида ЕСТЬNULL(A + B + C, 0) не работает так, как ожидает разработчик: если хотя бы один из операндов равен NULL, всё выражение внутри функции принимает значение NULL, и внешняя функция просто возвращает итоговый ноль.
  2. Сравнение с NULL в условиях ВЫБОР КОГДА. Если поле ВТ.Остатки содержит NULL (например, после левого соединения), то выражение ВТ.Остатки <> 0 или ВТ.Остатки > 0 возвращает значение НЕОПРЕДЕЛЕНО. Конструкция ВЫБОР воспринимает его как ЛОЖЬ и всегда выполняет ветку ИНАЧЕ.
  3. Несоответствие типов данных. Арифметические операции деления и умножения допустимы только для числовых типов. Если параметр &ПериодДней или поле ВТ.СрокДоставки переданы как Дата, а не Число, СУБД не сможет выполнить расчет или выдаст ошибку.
  4. Деление на ноль. Функция ЕСТЬNULL подменяет отсутствие значения, но не защищает от ситуаций, когда делитель физически равен числовому нулю (0).

Способ 1. Обертывание каждого слагаемого и множителя в ЕСТЬNULL

Рассмотрим пример ошибочного кода, в котором функция ЕСТЬNULL применена ко всему математическому выражению целиком:


ВЫБОР
	КОГДА ВТ.Остатки <> 0
		ТОГДА ЕСТЬNULL(ВТ.Продажи / &ПериодДней * ВТ.СрокДоставки + ВТ.МПО + ВТ.Продажи, 0)
	ИНАЧЕ ЕСТЬNULL(ВТ.Продажи / &ПериодДней * ВТ.СрокДоставки - ВТ.Остатки + ВТ.МПО + ВТ.Продажи, 0)
КОНЕЦ КАК Результат

Если хотя бы одно из полей (например, ВТ.МПО или ВТ.СрокДоставки) равно NULL, всё выражение обнуляется. Чтобы исправить эту ситуацию, необходимо применить ЕСТЬNULL к каждому полю отдельно, а также обработать условие сравнения:


ВЫБОР
	КОГДА ЕСТЬNULL(ВТ.Остатки, 0) <> 0
		ТОГДА ЕСТЬNULL(ВТ.Продажи, 0) / &ПериодДней * ЕСТЬNULL(ВТ.СрокДоставки, 0) + ЕСТЬNULL(ВТ.МПО, 0) + ЕСТЬNULL(ВТ.Продажи, 0)
	ИНАЧЕ ЕСТЬNULL(ВТ.Продажи, 0) / &ПериодДней * ЕСТЬNULL(ВТ.СрокДоставки, 0) - ЕСТЬNULL(ВТ.Остатки, 0) + ЕСТЬNULL(ВТ.МПО, 0) + ЕСТЬNULL(ВТ.Продажи, 0)
КОНЕЦ КАК Результат

Способ 2. Обработка NULL при формировании временных таблиц

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

Посмотрим на пример заменяющего выражения во временной таблице:


ВЫБРАТЬ
	Продажи.Номенклатура КАК Номенклатура,
	ЕСТЬNULL(Продажи.Количество, 0) КАК Продажи,
	ЕСТЬNULL(Остатки.Количество, 0) КАК Остатки,
	ЕСТЬNULL(Сроки.Дней, 0) КАК СрокДоставки,
	ЕСТЬNULL(МПО.Количество, 0) КАК МПО
ПОМЕСТИТЬ ВТ_Данные
ИЗ
	...

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

Способ 3. Проверка типов данных и защита от деления на ноль

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

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