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

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

Одной из наиболее распространенных ошибок при разработке отчетов и обработок в 1С является попытка деления на ноль внутри текста запроса. Типичная ситуация: мы пытаемся рассчитать среднюю цену, разделив сумму оборота на количество, но сталкиваемся с тем, что по определенным аналитическим разрезам количество может отсутствовать (быть равным нулю или иметь значение NULL). В этой статье мы подробно разберем, как грамотно обработать такие ситуации, чтобы запрос работал стабильно вне зависимости от наполненности базы данных.

Причина возникновения проблемы

Проанализируем ситуацию на примере работы с виртуальной таблицей ХозрасчетныйОборотыДтКт. При формировании проводок, особенно в бухгалтерском учете, возможны случаи, когда фиксируется только суммовой оборот без количественного. Если в запросе написать прямое деление:


| ХозрасчетныйОборотыДтКт.СуммаОборот / ХозрасчетныйОборотыДтКт.КоличествоОборотДт КАК Цена

СУБД (будь то Microsoft SQL Server или PostgreSQL) выдаст критическую ошибку "Divide by zero error", и выполнение запроса будет прервано. Наша задача — научить систему корректно реагировать на нулевые значения в знаменателе.

Метод 1: Использование универсальной конструкции ВЫБОР

Рассмотрим самый надежный и распространенный способ — использование условного оператора ВЫБОР. Этот метод позволяет проверить значение делителя перед выполнением арифметической операции. Посмотрим на пример реализации:


ВЫБОР
    КОГДА ХозрасчетныйОборотыДтКт.КоличествоОборотДт = 0
        ТОГДА 0
    ИНАЧЕ ХозрасчетныйОборотыДтКт.СуммаОборот / ХозрасчетныйОборотыДтКт.КоличествоОборотДт
КОНЕЦ КАК Цена

Важное преимущество: с точки зрения производительности SQL-сервера этот подход является оптимальным. Процессор базы данных сначала вычисляет логическое условие. Если условие истинно (количество равно нулю), он возвращает результат из ветки ТОГДА и даже не пытается выполнить операцию деления во второй ветке. Это гарантирует отсутствие ошибок на уровне СУБД.

Метод 2: Обработка значений NULL

Выясним причину, почему простой проверки на 0 иногда недостаточно. Если ваш запрос использует ЛЕВОЕ СОЕДИНЕНИЕ или обращается к полям, которые могут не содержать записей, в знаменателе может оказаться NULL. В языке запросов 1С любая операция с NULL возвращает NULL. Чтобы избежать непредсказуемого поведения, необходимо использовать функцию ЕстьNULL.

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


ВЫБОР
    КОГДА ЕстьNULL(ХозрасчетныйОборотыДтКт.КоличествоОборотДт, 0) = 0
        ТОГДА 0
    ИНАЧЕ ХозрасчетныйОборотыДтКт.СуммаОборот / ХозрасчетныйОборотыДтКт.КоличествоОборотДт
КОНЕЦ КАК Цена

Обратите внимание: во второй части выражения (после ИНАЧЕ) можно не использовать ЕстьNULL, если мы уже убедились в первой части, что значение не равно нулю. Однако для чистоты кода некоторые разработчики предпочитают оборачивать делитель в функцию ЕстьNULL в обоих случаях.

Метод 3: Исключение строк через ГДЕ

Иногда бизнес-логика подразумевает, что если количества нет, то и строка в отчете не нужна. В таком случае мы можем использовать фильтрацию в секции ГДЕ. Проанализируем два варианта:

  1. ГДЕ ХозрасчетныйОборотыДтКт.КоличествоОборотДт <> 0 — исключает только нулевые значения.
  2. ГДЕ ХозрасчетныйОборотыДтКт.КоличествоОборотДт > 0 — исключает нулевые и отрицательные значения (например, сторно).

Осторожно: этот метод может привести к тому, что данные "пропадут" из отчета. Если у вас есть сумма без количества, она просто не попадет в выборку, что приведет к расхождениям с оборотно-сальдовой ведомостью. Используйте этот способ только если вы четко понимаете, что пустые по количеству строки не несут ценности.

Особенности расчета цены в Системе компоновки данных (СКД)

Если вы разрабатываете отчет на базе СКД, расчет цены внутри текста запроса — не самая лучшая идея. Проблема заключается в расчете итогов. Если вы рассчитаете цену в каждой строке запроса, СКД при выводе итогов просто просуммирует эти цены, что математически неверно (средняя цена по складу не равна сумме цен всех товаров).

Правильнее будет вынести расчет в "Вычисляемые поля" или "Ресурсы" СКД. В поле выражения ресурса для поля Цена следует прописать:


ВЫБОР
    КОГДА Сумма(КоличествоОборот) <> 0
        ТОГДА Сумма(СуммаОборот) / Сумма(КоличествоОборот)
    ИНАЧЕ 0
КОНЕЦ

В этом случае система сначала просуммирует всю стоимость и всё количество, а затем выполнит деление, получив корректную средневзвешенную цену для группировок и общих итогов.

Технические нюансы: Точность и Округление

При делении в запросах важно учитывать разрядность результата. Иногда при делении очень маленьких сумм на большие количества получается число с большим количеством знаков после запятой. Если не ограничить точность, СУБД или механизмы 1С могут обрезать число некорректно.

Рекомендуется использовать функцию ВЫРАЗИТЬ для явного указания точности:


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

Математическая логика: что возвращать?

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

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

← На главную