При работе в клиент-серверном варианте на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с неприятной особенностью: данные в детальных записях запроса отображаются корректно, но при использовании функции СУММА и конструкции СГРУППИРОВАТЬ ПО точность дробной части внезапно снижается. Это может привести к серьезным ошибкам в расчетах, особенно в таких задачах, как учет себестоимости, расчет налогов или работа с очень мелкими единицами измерения (граммы, миллилитры).
В этой статье мы подробно разберем, почему возникает такая ситуация, как SQL Server обрабатывает числа и какие конкретные шаги нужно предпринять, чтобы гарантировать сохранение всех знаков после запятой в ваших отчетах и обработках.
Рассмотрим классическую ситуацию, описанную в практике разработки. У нас есть регистр накопления (например, Тест) с ресурсом Количество, имеющим точность 9 знаков после запятой. В регистре всего одна запись со значением -0,003494665.
Если мы выполним простой запрос на выборку данных, результат будет верным:
ВЫБРАТЬ
ТестОбороты.Товар КАК Товар,
ТестОбороты.КоличествоОборот КАК КоличествоОборот
ИЗ
РегистрНакопления.Тест.Обороты КАК ТестОбороты
Результат: -0,003494665.
Однако, как только мы добавляем агрегацию и группировку, платформа выдает округленное значение:
ВЫБРАТЬ
ТестОбороты.Товар КАК Товар,
СУММА(ТестОбороты.КоличествоОборот) КАК КоличествоОборот
ИЗ
РегистрНакопления.Тест.Обороты КАК ТестОбороты
СГРУППИРОВАТЬ ПО
ТестОбороты.Товар
Результат: -0,00349467 (округлилось до 8 знаков). Почему так происходит? Выясним причину.
Разберем ситуацию изнутри. Когда платформа 1С выполняет запрос, она транслирует его в язык T-SQL, понятный для СУБД (например, Microsoft SQL Server). Функция СУММА() в 1С превращается в SUM() в SQL.
Проблема заключается в том, что тип данных результата функции SUM() в SQL Server определяется типом входного выражения. Если платформа 1С передает данные во временные таблицы или промежуточные вычисления без явного указания точности, SQL Server может применить правила неявного приведения типов. Существует несколько ключевых факторов:
NUMERIC с фиксированной шкалой, которая меньше вашей.Обороты, усложняет ситуацию, так как платформа сама строит сложный подзапрос для получения итогов, и на каждом этапе вложенности может происходить потеря точности.Проанализируем наиболее эффективный способ решения проблемы — использование функции ВЫРАЗИТЬ. Эта функция заставляет платформу 1С добавить в SQL-код явную команду CAST или CONVERT, указывая СУБД точную разрядность числа.
Чтобы исправить ошибку округления, нам нужно обернуть суммируемое поле в ВЫРАЗИТЬ прямо внутри агрегатной функции:
ВЫБРАТЬ
ТестОбороты.Товар КАК Товар,
СУММА(ВЫРАЗИТЬ(ТестОбороты.КоличествоОборот КАК ЧИСЛО(22, 9))) КАК КоличествоОборот
ИЗ
РегистрНакопления.Тест.Обороты КАК ТестОбороты
СГРУППИРОВАТЬ ПО
ТестОбороты.Товар
Важный нюанс: Указывайте размерность точно такую же, как у ресурса в метаданных (в нашем примере это ЧИСЛО(22, 9)). Это гарантирует, что SQL Server выделит достаточно места для хранения промежуточных сумм без потери знаков.
В некоторых случаях, особенно при работе со старыми версиями платформы или специфическими настройками СУБД, метод с ВЫРАЗИТЬ может не давать 100% гарантии (например, при очень сложных вложенных расчетах). Тогда мы можем применить метод масштабирования.
Рассмотрим этот метод по шагам:
Пример реализации в коде запроса:
ВЫБРАТЬ
ТестОбороты.Товар КАК Товар,
СУММА(ТестОбороты.КоличествоОборот * 1000000) / 1000000 КАК КоличествоОборот
ИЗ
РегистрНакопления.Тест.Обороты КАК ТестОбороты
СГРУППИРОВАТЬ ПО
ТестОбороты.Товар
Этот подход "обманывает" механизм округления SQL Server, так как основные вычисления происходят с числами, у которых значащие цифры перенесены в целую часть.
Если ваш запрос сложный и включает использование временных таблиц, крайне важно следить за типами данных на каждом этапе. Если вы помещаете данные во временную таблицу без ВЫРАЗИТЬ, колонка в tempdb на стороне SQL может создаться с точностью по умолчанию (обычно до 6 знаков).
Посмотрим на правильный пример создания временной таблицы:
ВЫБРАТЬ
ВЫРАЗИТЬ(ТестОбороты.КоличествоОборот КАК ЧИСЛО(22, 9)) КАК Количество,
ТестОбороты.Товар КАК Товар
ПОМЕСТИТЬ ВТ_Данные
ИЗ
РегистрНакопления.Тест.Обороты КАК ТестОбороты;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СУММА(ВТ_Данные.Количество) КАК ИтоговоеКоличество,
ВТ_Данные.Товар
ИЗ
ВТ_Данные КАК ВТ_Данные
СГРУППИРОВАТЬ ПО
ВТ_Данные.Товар
Многие разработчики замечают, что в файловой базе данных (локальной) проблема не воспроизводится. Это объясняется тем, что файловый движок 1С использует собственные алгоритмы обработки чисел, которые более лояльны к высокой точности и не всегда следуют строгим правилам типизации ANSI SQL. Однако ориентация на файловую версию при разработке — плохая практика, так как большинство рабочих систем функционирует на SQL Server, где типизация критична.
Подведем итоги и сформируем правила, которые помогут избежать ошибок в будущем:
ВЫРАЗИТЬ внутри агрегатных функций СУММА, СРЕДНЕЕ, если точность ресурса превышает 2-3 знака после запятой.ВЫРАЗИТЬ — ваш единственный надежный инструмент.Соблюдение этих простых правил позволит вам избежать расхождений в отчетах и обеспечит корректную работу алгоритмов расчета в любой среде эксплуатации.