Как правильно реализовать обход ошибки «деление на ноль» в запросах 1С?

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

При разработке сложных отчетов или обработок на платформе «1С:Предприятие» программисты часто сталкиваются с необходимостью вычисления производных показателей, таких как средняя цена, себестоимость единицы продукции или процент выполнения плана. Все эти операции объединяет одно математическое действие — деление. Если в качестве делителя выступает ресурс, который может принимать нулевое значение (например, остаток товара или объем продаж), система выдаст критическую ошибку «деление на ноль».

В этой статье мы подробно разберем, почему стандартные попытки «обойти» эту проблему не всегда работают, проанализируем ошибки в логике построения условий ВЫБОР и рассмотрим комплексный подход к безопасному делению в запросах 1С.

Разбор типичной ошибки в конструкции ВЫБОР

Рассмотрим ситуацию, с которой столкнулся автор темы. В его запросе использовалась следующая конструкция:


ВЫБОР
    КОГДА КХМ_УправленческийОстатки.КоличествоОстаток = ВТ_в.Количество
        ТОГДА КХМ_УправленческийОстатки.СуммаУУОстаток
    ИНАЧЕ ВЫБОР
            КОГДА КХМ_УправленческийОстатки.СуммаУУОстаток / КХМ_УправленческийОстатки.КоличествоОстаток = 0
                ТОГДА 0
            ИНАЧЕ ВТ_в.Количество * КХМ_УправленческийОстатки.СуммаУУОстаток / КХМ_УправленческийОстатки.КоличествоОстаток
        КОНЕЦ
КОНЕЦ

Давайте выясним причину, по которой этот код продолжает вызывать ошибку. Проблема кроется в строке: КОГДА КХМ_УправленческийОстатки.СуммаУУОстаток / КХМ_УправленческийОстатки.КоличествоОстаток = 0. Для того чтобы СУБД смогла сравнить результат деления с нулем, она сначала должна выполнить это деление. Если поле КоличествоОстаток равно нулю, выполнение прервется именно в этой точке, так и не дойдя до ветки ИНАЧЕ.

Важное правило: Проверка делителя должна осуществляться ДО того, как над ним будет произведена любая арифметическая операция деления.

Решение №1: Прямая проверка делителя на ноль

Самый простой и эффективный способ — заменить проверку результата деления на проверку самого поля-делителя. Исходя из советов коллег на форуме, мы можем переписать запрос следующим образом:


ВЫБОР
    КОГДА КХМ_УправленческийОстатки.КоличествоОстаток = 0
        ТОГДА 0
    КОГДА КХМ_УправленческийОстатки.КоличествоОстаток = ВТ_в.Количество
        ТОГДА КХМ_УправленческийОстатки.СуммаУУОстаток
    ИНАЧЕ ВТ_в.Количество * КХМ_УправленческийОстатки.СуммаУУОстаток / КХМ_УправленческийОстатки.КоличествоОстаток
КОНЕЦ

В данном случае мы сразу отсекаем ситуацию с нулевым знаменателем. Если КоличествоОстаток равно 0, запрос вернет 0 и не будет пытаться вычислять остальные ветки конструкции ВЫБОР.

Решение №2: Обработка значений NULL

При работе с левыми соединениями (ЛЕВОЕ СОЕДИНЕНИЕ) часто возникает ситуация, когда запись в правой таблице отсутствует. В этом случае все числовые поля этой таблицы будут содержать значение NULL. Хотя деление на NULL в большинстве СУБД не вызывает фатальной ошибки (результат будет NULL), это может привести к некорректным итогам в отчете или ошибкам в коде 1С при чтении результата запроса.

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


ВЫБОР
    КОГДА ЕСТЬNULL(КХМ_УправленческийОстатки.КоличествоОстаток, 0) = 0
        ТОГДА 0
    ИНАЧЕ ВТ_в.Количество * КХМ_УправленческийОстатки.СуммаУУОстаток / КХМ_УправленческийОстатки.КоличествоОстаток
КОНЕЦ

Использование функции ЕСТЬNULL гарантирует, что даже при отсутствии данных в таблице остатков система воспримет это как 0 и корректно отработает условие ВЫБОР.

Решение №3: Управление точностью вычислений (ВЫРАЗИТЬ)

При делении в запросах результат может иметь неопределенную разрядность. Если полученное число затем участвует в расчете итогов или выводится в отчет, это может привести к избыточному количеству знаков после запятой или даже к ошибке переполнения числового типа в SQL.

Проанализируем ситуацию: нам необходимо получить среднюю стоимость, округленную до двух знаков. Для этого мы применим конструкцию ВЫРАЗИТЬ:


ВЫРАЗИТЬ(
    ВЫБОР
        КОГДА КХМ_УправленческийОстатки.КоличествоОстаток = 0
            ТОГДА 0
        ИНАЧЕ КХМ_УправленческийОстатки.СуммаУУОстаток / КХМ_УправленческийОстатки.КоличествоОстаток
    КОНЕЦ 
    КАК ЧИСЛО(15, 2)
)

Этот подход позволяет не только избежать ошибки деления на ноль, но и сразу подготовить данные для корректного отображения пользователю и хранения в базе данных.

Особенности работы SQL-сервера и «ленивые вычисления»

Нужно учитывать, что оптимизатор запросов SQL-сервера может пытаться выполнить вычисления во всех ветках оператора CASE (аналог ВЫБОР в 1С), если посчитает это более быстрым путем. Чтобы минимизировать риски, придерживайтесь следующих рекомендаций:

  1. Приоритетность условий: Всегда ставьте проверку на ноль самым первым условием в конструкции ВЫБОР.
  2. Упрощение условий: Не загромождайте одну конструкцию ВЫБОР десятками вложенных условий. Если логика расчета сложная (как в примере сообщения 0), лучше сначала подготовить «чистые» данные во временной таблице.
  3. Использование временных таблиц: Если вам нужно делить на значение, которое само является результатом сложного расчета, сначала вычислите это значение во временной таблице, а в итоговом запросе просто выполните проверку КОГДА Поле = 0.

Работа с делением в Системе компоновки данных (СКД)

Если вы выполняете расчет не в самом тексте запроса, а в вычисляемых полях или ресурсах СКД, механизмы платформы работают несколько иначе. В выражениях СКД также доступна функция Выбор, синтаксис которой идентичен запросу. Однако помните, что СКД часто прощает деление на ноль, просто возвращая пустое значение в ячейке отчета. Тем не менее, для корректного расчета агрегатных функций (Сумма, Среднее) по группировкам, явная обработка нуля через Выбор остается обязательной практикой.

Резюмируем: Для надежного решения проблемы деления на ноль всегда проверяйте делитель на равенство 0 и на NULL до выполнения операции, а результат деления ограничивайте по точности с помощью функции ВЫРАЗИТЬ.

← На главную