В процессе работы с управляемыми формами списков документов пользователи могут столкнуться с ситуацией, когда список заказов перестает открываться по одному конкретному контрагенту. При этом система генерирует критическую ошибку СУБД: 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). Переполнение происходит в следующих случаях:
COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION является следствием арифметического сбоя — SQL Server при переполнении прерывает пакет и откатывает транзакцию, после чего платформа 1С пытается зафиксировать уже закрытую транзакцию.Посмотрим на пример запроса динамического списка. В типовых доработках в список заказов покупателя часто включают расчетные колонки. Найдем в свойствах динамического списка флаг Произвольный запрос и проанализируем секцию ВЫБРАТЬ:
ВЫБРАТЬ
ДокументЗаказПокупателя.Ссылка КАК Ссылка,
ДокументЗаказПокупателя.Номер КАК Номер,
ДокументЗаказПокупателя.Дата КАК Дата,
ДокументЗаказПокупателя.СуммаДокумента КАК СуммаДокумента,
ВЫРАЗИТЬ(ВЫБОР
КОГДА СуммаПеревода.СуммаПеревод ЕСТЬ NULL
ТОГДА 100
КОГДА ДокументЗаказПокупателя.СуммаДокумента ЕСТЬ NULL
ИЛИ ДокументЗаказПокупателя.СуммаДокумента = 0
ТОГДА 0
КОГДА ДокументЗаказПокупателя.Курс ЕСТЬ NULL
ИЛИ ДокументЗаказПокупателя.Курс = 0
ТОГДА 0
ИНАЧЕ (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс - СуммаПеревода.СуммаПеревод) /
(ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) * 100
КОНЕЦ КАК ЧИСЛО(6, 2)) КАК Рентабельность
ИЗ
Документ.ЗаказПокупателя КАК ДокументЗаказПокупателя
ЛЕВОЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
ЗаказПокупателяПереводчики.Ссылка КАК Ссылка,
СУММА(ЕСТЬNULL(ЗаказПокупателяПереводчики.СуммаВал, 0)) КАК СуммаПеревод
ИЗ
Документ.ЗаказПокупателя.rsПереводчики КАК ЗаказПокупателяПереводчики
СГРУППИРОВАТЬ ПО
ЗаказПокупателяПереводчики.Ссылка
) КАК СуммаПеревода
ПО ДокументЗаказПокупателя.Ссылка = СуммаПеревода.Ссылка
Для быстрой проверки гипотезы переполнения временно заменим расчетную часть в ветке ИНАЧЕ константой 0:
// Временная заглушка для проверки
ИНАЧЕ 0
Если после этого список по проблемному контрагенту открывается без сбоев, источник ошибки определен верно — сбой вызывает приведение вычисленного значения к слишком узкому числовому типу.
Разберем способ устранения проблемы. Чтобы исключить переполнение разрядности при любых соотношениях выручки и затрат, увеличим длину числового поля в конструкции ВЫРАЗИТЬ с ЧИСЛО(6, 2) до ЧИСЛО(15, 2).
Откроем форму списка документов в конфигураторе, перейдем в свойства динамического списка и заменим блок вычисления поля на корректный:
ВЫРАЗИТЬ(ВЫБОР
КОГДА СуммаПеревода.СуммаПеревод ЕСТЬ NULL
ТОГДА 100
КОГДА ДокументЗаказПокупателя.СуммаДокумента ЕСТЬ NULL
ИЛИ ДокументЗаказПокупателя.СуммаДокумента = 0
ТОГДА 0
КОГДА ДокументЗаказПокупателя.Курс ЕСТЬ NULL
ИЛИ ДокументЗаказПокупателя.Курс = 0
ТОГДА 0
ИНАЧЕ (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс - СуммаПеревода.СуммаПеревод) /
(ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) * 100
КОНЕЦ КАК ЧИСЛО(15, 2)) КАК Рентабельность
Тип ЧИСЛО(15, 2) позволяет хранить до 13 знаков в целой части, что с запасом перекрывает любые экстремальные значения относительных показателей в учетной системе и полностью устраняет ошибку СУБД.
Рассмотрим дополнительную оптимизацию формулы. Помимо увеличения разрядности, рекомендуется обезопасить запрос от деления на околонулевые значения, которые могут возникать из-за ошибок округления валютных сумм:
ВЫРАЗИТЬ(ВЫБОР
КОГДА СуммаПеревода.СуммаПеревод ЕСТЬ NULL
ТОГДА 100
// Проверяем знаменатель на минимально допустимый порог
КОГДА (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) < 0.01
ТОГДА 0
ИНАЧЕ (ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс - СуммаПеревода.СуммаПеревод) /
(ДокументЗаказПокупателя.СуммаДокумента * ДокументЗаказПокупателя.Курс) * 100
КОНЕЦ КАК ЧИСЛО(15, 2)) КАК Рентабельность
Проанализируем архитектурные аспекты решения. Расчет сложных показателей с группировками по табличным частям непосредственно в основном запросе динамического списка создает повышенную нагрузку на СУБД и приводит к замедлению работы интерфейса. Для стабильной работы рекомендуется применять следующие практики:
ПриПолученииДанныхНаСервере: вместо вычисления рентабельности в запросе списка рассчитывайте значения только для порции строк, реально видимых пользователю на экране.ПЕРВЫЕ 50 и отбор по конкретному контрагенту, чтобы сымитировать постраничную работу платформы и оперативно обнаружить некорректные данные.