При работе с базами данных в среде 1С:Предприятие программисты нередко сталкиваются с критической ошибкой выполнения операции: Microsoft OLE DB Driver for SQL Server: Обнаружена ошибка: деление на ноль. HRESULT=80040E14, SQLSrvr: SQLSTATE=22012. Эта ситуация полностью блокирует работу пользователя, прерывает проведение документов или формирование отчетов. В рамках данной статьи мы подробно разберем, почему возникает эта ошибка, как быстро найти «битые» данные и как модифицировать запрос, чтобы сделать его устойчивым к подобным сбоям.
Для начала разберем техническую природу сообщения SQLSTATE=22012. Система SQL Server, на которой чаще всего работают серверные базы 1С, крайне строго относится к арифметическим операциям. В отличие от некоторых языков программирования, которые могут возвращать бесконечность или NaN, SQL Server при попытке деления любого числа на абсолютный ноль немедленно прерывает транзакцию и возвращает ошибку с кодом native=8134.
Важно понимать различие между NULL и 0. Рассмотрим ситуацию: если в вашем запросе делитель имеет значение NULL, то результатом операции деления также будет NULL, и ошибка не возникнет. Однако, если в поле записано числовое значение 0, выполнение запроса будет остановлено. Проанализируем типичный сценарий: в справочнике единиц измерения или упаковок поле Знаменатель (или Коэффициент) по логике системы должно быть равно единице или более. Если в результате некорректного импорта данных, сбоя при программном создании записи или ошибки пользователя туда попал ноль — любая попытка пересчета количества в базовые единицы приведет к фатальной ошибке.
Выясним причину, по которой конкретный документ (например, «Списание на ремонт») выдает ошибку. Если ошибка возникает только при определенных условиях, значит, в запросе участвуют данные, где делитель равен нулю. Рассмотрим, как найти такие записи с помощью консоли запросов (удобно через инструменты анализа структуры данных и консоль запросов). Чаще всего проблема кроется в справочниках, связанных с единицами измерения.
Выполним следующий проверочный запрос в консоли:
ВЫБРАТЬ
ЕдиницыИзмерения.Ссылка КАК ПроблемнаяЕдиница,
ЕдиницыИзмерения.Владелец КАК Номенклатура,
ЕдиницыИзмерения.Знаменатель КАК Знаменатель
ИЗ
Справочник.УпаковкиЕдиницыИзмерения КАК ЕдиницыИзмерения
ГДЕ
ЕдиницыИзмерения.Знаменатель = 0
Если запрос вернул строки, значит, мы нашли корень проблемы. Необходимо вручную или программно установить корректные значения (обычно 1) для этих записей — в этом поможет универсальный редактор для поиска и массового изменения данных в 1С. Обратите внимание, что в некоторых конфигурациях (УТ 11, ERP) упаковки могут храниться в разных справочниках, поэтому стоит проверить все связанные метаданные.
Даже если мы исправили данные в справочнике, хорошим тоном программирования в 1С является создание «неубиваемых» запросов. Мы не можем гарантировать, что завтра пользователь снова не создаст «нулевую» упаковку (для предотвращения этого подойдёт настройка автоматического контроля данных без программирования). Проанализируем, как изменить логику вычислений прямо в тексте запроса. Для этого используется конструкция ВЫБОР.
Рассмотрим пример. Допустим, у нас есть формула расчета: Количество / Знаменатель. Чтобы обезопасить её, перепишем выражение следующим образом:
ВЫБОР
КОГДА Таблица.Знаменатель = 0
ТОГДА Таблица.Количество / 1
ИНАЧЕ Таблица.Количество / Таблица.Знаменатель
КОНЕЦ
В данном случае мы проверяем условие равенства нулю. Если условие истинно, мы подменяем делитель на единицу (или возвращаем 0, в зависимости от бизнес-логики), тем самым избегая исключительной ситуации на уровне SQL Server.
Часто данные в запрос попадают через ЛЕВОЕ СОЕДИНЕНИЕ. В этом случае, если соответствие не найдено, в поле делителя придет NULL. Как мы уже выяснили, деление на NULL не вызывает ошибку, но результат будет неопределенным. Чтобы обеспечить предсказуемость, разберем совместное использование функций ЕСТЬNULL и ВЫБОР.
Посмотрим на пример комплексной защиты:
ВЫБОР
КОГДА ЕСТЬNULL(Таблица.Коэффициент, 0) = 0
ТОГДА 1
ИНАЧЕ Таблица.Коэффициент
КОНЕЦ
Важное замечание: никогда не используйте просто ЕСТЬNULL(Поле, 0) в знаменателе без последующей проверки на ноль, так как если исходное поле было NULL, функция превратит его в 0, и вы гарантированно получите ошибку native=8134.
Проанализируем ситуацию, упомянутую автором темы. При списании на ремонт «комплектом» система часто производит разукомплектацию или распределение стоимости. Если для комплектующих не заданы весовые коэффициенты или доли стоимости, либо если сама единица измерения комплекта не имеет заполненных параметров пересчета, в формулу распределения попадает ноль.
Разберем по шагам, что нужно проверить в конфигурации:
Для предотвращения подобных ситуаций в будущем рекомендуем придерживаться следующих правил:
Объект.Знаменатель = Макс(1, ПереданноеЗначение) — для автоматизации этой проверки подойдёт модуль контроля ввода данных при записи объектов.ВЫБОР.Таким образом, решение проблемы «деления на ноль» всегда состоит из двух этапов: оперативное исправление данных в базе (поиск нулей через консоль запросов) и архитектурная правка кода (добавление проверок ВЫБОР...КОГДА...), чтобы исключить повторение ситуации.