Как исправить ошибку деления на ноль в запросе 1С

Программист IT и автоматизация бизнеса
← На главную

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

Разберем подробнее причины возникновения этой ошибки и проанализируем практические способы её устранения.

Выясним причины возникновения ошибки

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

  1. Множественное последовательное деление: Когда формула содержит деление деления (например, A / (B / C) / (D / E)), вероятность получения нуля или потери точности в промежуточных вычислениях резко возрастает. Если промежуточный делитель из-за ограниченной разрядности округляется до 0, происходит ошибка.
  2. Особенности оптимизатора MS SQL Server / PostgreSQL: Оптимизатор СУБД самостоятельно строит дерево выполнения запроса. Операции вычисления значений столбцов в блоке ВЫБРАТЬ могут физически выполняться раньше, чем фильтрация строк в блоке ГДЕ или проверка условий в ВЫБОР.
  3. Неявное поведение при наличии NULL: Если одно из полей содержит значение NULL, математические операции с ним могут приводить к неожиданным результатам при отсутствии явной обработки с помощью функции ЕСТЬNULL().

Способ 1. Упрощение математической формулы

Рассмотрим математическое преобразование выражения. Предположим, у нас есть исходное выражение с каскадом делений:

ДнейПросрочки / (СуммаОстаток / СуммаОстаток2) / (СуммаОстаток / СуммаОстаток1)

Вспомним правила алгебры: деление на дробь равносильно умножению на перевернутую дробь. Преобразуем выражение последовательно:

  1. A / (B / C) = (A * C) / B
  2. ((A * C) / B) / (B / D) = (A * C * D) / (B * B)

В итоге вместо четырех операций деления мы получаем всего одну операцию деления! Знаменателем формулы становится квадрат единственной переменной СуммаОстаток * СуммаОстаток, и нам достаточно проверить на ноль всего один показатель вместо трех.

Посмотрим на пример упрощенного запроса:


ВЫБОР
	КОГДА ВложенныйЗапрос.СуммаВзаиморасчетовОстаток = 0
		ТОГДА 0
	ИНАЧЕ ВложенныйЗапрос.ДнейПросрочкиВсего * ВложенныйЗапрос2.СуммаВзаиморасчетовОстаток * ВложенныйЗапрос1.СуммаВзаиморасчетовОстаток / (ВложенныйЗапрос.СуммаВзаиморасчетовОстаток * ВложенныйЗапрос.СуммаВзаиморасчетовОстаток)
КОНЕЦ КАК ПрКонтрактная

Способ 2. Объединение условий с помощью логического ИЛИ

Вместо создания глубоких вложенных инструкций ВЫБОР КОГДА ... ТОГДА ВЫБОР КОГДА ... следует объединять все проверки делителей в одну ветку с помощью оператора ИЛИ. Это делает код читаемым и снижает вероятность ошибки.

Разберем пример реализации такого подхода:


ВЫБОР
	КОГДА ВложенныйЗапрос.СуммаВзаиморасчетовОстаток = 0 
		ИЛИ ВложенныйЗапрос2.СуммаВзаиморасчетовОстаток = 0 
		ИЛИ ВложенныйЗапрос1.СуммаВзаиморасчетовОстаток = 0
		ТОГДА 0
	ИНАЧЕ ВложенныйЗапрос.ДнейПросрочкиВсего / (ВложенныйЗапрос.СуммаВзаиморасчетовОстаток / ВложенныйЗапрос2.СуммаВзаиморасчетовОстаток) / (ВложенныйЗапрос.СуммаВзаиморасчетовОстаток / ВложенныйЗапрос1.СуммаВзаиморасчетовОстаток)
КОНЕЦ КАК ПрКонтрактная

Способ 3. Безопасная обработка NULL-значений

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

Проанализируем пример кода с проверкой на NULL:


ВЫБОР
	КОГДА ЕСТЬNULL(ВложенныйЗапрос1.СуммаВзаиморасчетовОстаток * ВложенныйЗапрос.СуммаВзаиморасчетовОстаток, 0) = 0
		ТОГДА 0
	ИНАЧЕ ВложенныйЗапрос.ДнейПросрочкиВсего * ВложенныйЗапрос2.СуммаВзаиморасчетовОстаток * ВложенныйЗапрос1.СуммаВзаиморасчетовОстаток / (ВложенныйЗапрос.СуммаВзаиморасчетовОстаток * ВложенныйЗапрос.СуммаВзаиморасчетовОстаток)
КОНЕЦ КАК ПрКонтрактная

Способ 4. Использование временных таблиц для изоляции расчетов

Если СУБД всё равно оптимизирует порядок вычислений и вызывает ошибку деления до фильтрации данных, рекомендуется разбить запрос на пакет из двух частей:

  1. В первом запросе отобрать исходные данные, очистить их от нулей и поместить во временную таблицу с помощью конструкции ПОМЕСТИТЬ ВТ_ПодготовленныеДанные.
  2. Во втором запросе пакета выполнить окончательный расчет деления, выбирая данные из сформированной временной таблицы.

Материализация промежуточных данных во временной таблице гарантирует, что оптимизатор SQL-сервера не станет выполнять деление для строк с нулевым знаменателем.

← На главную