Как исправить ошибку Arithmetic overflow error converting numeric при открытии заказов контрагента в 1С

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

В процессе работы с управляемыми формами списков документов пользователи могут столкнуться с ситуацией, когда список заказов перестает открываться по одному конкретному контрагенту. При этом система генерирует критическую ошибку СУБД: Microsoft SQL Server Native Client: Arithmetic overflow error converting numeric to data type numeric. Если база данных работает в файловом режиме, платформа 1С выдает аналогичное сообщение: Ошибка SQL: Переполнение поля. Выясним причину такого поведения и разберем по шагам, как локализовать и устранить сбой.

Причины возникновения ошибки на уровне СУБД

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

В тексте запроса разработчики нередко рассчитывают вычисляемые поля «на лету» — например, прибыль, процент оплаты или рентабельность, приводя итоговый результат к строго определенному типу через конструкцию ВЫРАЗИТЬ(... КАК ЧИСЛО(Д, Т)). При трансляции запроса 1С в язык СУБД эта конструкция преобразуется в команду CONVERT(NUMERIC(Д, Т), ...) в Microsoft SQL Server.

Длина целой части числа в СУБД строго ограничена разностью разрядности и точности: Длина - Точность. Если для рентабельности задано приведение к типу ЧИСЛО(6, 2), то на целую часть отводится всего 4 знака (максимально допустимое значение от -9999.99 до 9999.99). Переполнение происходит в следующих случаях:

  1. Глубоко убыточные сделки: если затраты по заказу многократно превышают выручку, формула рентабельности дает отрицательный процент, выходящий за границы диапазона -9999.99% (например, при затратах 50 000 руб. и выручке 100 руб. результат составит -49900%).
  2. Сверхмалые суммы или околонулевые значения: при делении на очень маленькое число из-за погрешностей округлений расчетный процент мгновенно стремится к бесконечности.
  3. Ошибочные сопутствующие сбои транзакций: ошибка COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION является следствием арифметического сбоя — SQL Server при переполнении прерывает пакет и откатывает транзакцию, после чего платформа 1С пытается зафиксировать уже закрытую транзакцию.

Шаг 1. Локализация проблемного поля в запросе

Посмотрим на пример запроса динамического списка. В типовых доработках в список заказов покупателя часто включают расчетные колонки. Найдем в свойствах динамического списка флаг Произвольный запрос и проанализируем секцию ВЫБРАТЬ:


ВЫБРАТЬ
    ДокументЗаказПокупателя.Ссылка КАК Ссылка,
    ДокументЗаказПокупателя.Номер КАК Номер,
    ДокументЗаказПокупателя.Дата КАК Дата,
    ДокументЗаказПокупателя.СуммаДокумента КАК СуммаДокумента,
    ВЫРАЗИТЬ(ВЫБОР
            КОГДА СуммаПеревода.СуммаПеревод ЕСТЬ NULL 
                ТОГДА 100
            КОГДА ДокументЗаказПокупателя.СуммаДокумента ЕСТЬ NULL 
                ИЛИ ДокументЗаказПокупателя.СуммаДокумента = 0 
                ТОГДА 0
            КОГДА ДокументЗаказПокупателя.Курс ЕСТЬ NULL 
                ИЛИ ДокументЗаказПокупателя.Курс = 0 
                ТОГДА 0    
            ИНАЧЕ (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс - СуммаПеревода.СуммаПеревод) /
                  (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) * 100
        КОНЕЦ КАК ЧИСЛО(6, 2)) КАК Рентабельность
ИЗ
    Документ.ЗаказПокупателя КАК ДокументЗаказПокупателя
    ЛЕВОЕ СОЕДИНЕНИЕ (
        ВЫБРАТЬ
            ЗаказПокупателяПереводчики.Ссылка КАК Ссылка,
            СУММА(ЕСТЬNULL(ЗаказПокупателяПереводчики.СуммаВал, 0)) КАК СуммаПеревод
        ИЗ
            Документ.ЗаказПокупателя.rsПереводчики КАК ЗаказПокупателяПереводчики
        СГРУППИРОВАТЬ ПО
            ЗаказПокупателяПереводчики.Ссылка
    ) КАК СуммаПеревода
    ПО ДокументЗаказПокупателя.Ссылка = СуммаПеревода.Ссылка

Для быстрой проверки гипотезы переполнения временно заменим расчетную часть в ветке ИНАЧЕ константой 0:


// Временная заглушка для проверки
ИНАЧЕ 0

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

Шаг 2. Исправление ошибки в конфигураторе

Разберем способ устранения проблемы. Чтобы исключить переполнение разрядности при любых соотношениях выручки и затрат, увеличим длину числового поля в конструкции ВЫРАЗИТЬ с ЧИСЛО(6, 2) до ЧИСЛО(15, 2).

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


ВЫРАЗИТЬ(ВЫБОР
        КОГДА СуммаПеревода.СуммаПеревод ЕСТЬ NULL 
            ТОГДА 100
        КОГДА ДокументЗаказПокупателя.СуммаДокумента ЕСТЬ NULL 
            ИЛИ ДокументЗаказПокупателя.СуммаДокумента = 0 
            ТОГДА 0
        КОГДА ДокументЗаказПокупателя.Курс ЕСТЬ NULL 
            ИЛИ ДокументЗаказПокупателя.Курс = 0 
            ТОГДА 0    
        ИНАЧЕ (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс - СуммаПеревода.СуммаПеревод) /
              (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) * 100
    КОНЕЦ КАК ЧИСЛО(15, 2)) КАК Рентабельность

Тип ЧИСЛО(15, 2) позволяет хранить до 13 знаков в целой части, что с запасом перекрывает любые экстремальные значения относительных показателей в учетной системе и полностью устраняет ошибку СУБД.

Шаг 3. Защита от деления на малые величины в запросе

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


ВЫРАЗИТЬ(ВЫБОР
        КОГДА СуммаПеревода.СуммаПеревод ЕСТЬ NULL 
            ТОГДА 100
        // Проверяем знаменатель на минимально допустимый порог
        КОГДА (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) < 0.01 
            ТОГДА 0
        ИНАЧЕ (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс - СуммаПеревода.СуммаПеревод) /
              (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) * 100
    КОНЕЦ КАК ЧИСЛО(15, 2)) КАК Рентабельность

Рекомендации по архитектуре динамических списков

Проанализируем архитектурные аспекты решения. Расчет сложных показателей с группировками по табличным частям непосредственно в основном запросе динамического списка создает повышенную нагрузку на СУБД и приводит к замедлению работы интерфейса. Для стабильной работы рекомендуется применять следующие практики:

  1. Использование обработчика ПриПолученииДанныхНаСервере: вместо вычисления рентабельности в запросе списка рассчитывайте значения только для порции строк, реально видимых пользователю на экране.
  2. Денормализация данных: выполняйте расчет прибыли и рентабельности в момент проведения документа с сохранением итогов в реквизитах шапки или специализированном регистре сведений. В этом случае динамический список будет быстро читать готовые числа без математических операций на стороне SQL.
  3. Проверка через консоль запросов: при отладке запросов динамического списка в консоли всегда задавайте ограничение выборки ПЕРВЫЕ 50 и отбор по конкретному контрагенту, чтобы сымитировать постраничную работу платформы и оперативно обнаружить некорректные данные.
← На главную