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