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