Почему в запросе 1С при группировке происходит округление чисел и как сохранить точность?

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

При работе в клиент-серверном варианте на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с неприятной особенностью: данные в детальных записях запроса отображаются корректно, но при использовании функции СУММА и конструкции СГРУППИРОВАТЬ ПО точность дробной части внезапно снижается. Это может привести к серьезным ошибкам в расчетах, особенно в таких задачах, как учет себестоимости, расчет налогов или работа с очень мелкими единицами измерения (граммы, миллилитры).

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

Анализ проблемы на примере

Рассмотрим классическую ситуацию, описанную в практике разработки. У нас есть регистр накопления (например, Тест) с ресурсом Количество, имеющим точность 9 знаков после запятой. В регистре всего одна запись со значением -0,003494665.

Если мы выполним простой запрос на выборку данных, результат будет верным:


ВЫБРАТЬ
    ТестОбороты.Товар КАК Товар,
    ТестОбороты.КоличествоОборот КАК КоличествоОборот
ИЗ
    РегистрНакопления.Тест.Обороты КАК ТестОбороты

Результат: -0,003494665.

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


ВЫБРАТЬ
    ТестОбороты.Товар КАК Товар,
    СУММА(ТестОбороты.КоличествоОборот) КАК КоличествоОборот
ИЗ
    РегистрНакопления.Тест.Обороты КАК ТестОбороты
СГРУППИРОВАТЬ ПО
    ТестОбороты.Товар

Результат: -0,00349467 (округлилось до 8 знаков). Почему так происходит? Выясним причину.

Техническая причина: особенности трансляции запросов в SQL Server

Разберем ситуацию изнутри. Когда платформа 1С выполняет запрос, она транслирует его в язык T-SQL, понятный для СУБД (например, Microsoft SQL Server). Функция СУММА() в 1С превращается в SUM() в SQL.

Проблема заключается в том, что тип данных результата функции SUM() в SQL Server определяется типом входного выражения. Если платформа 1С передает данные во временные таблицы или промежуточные вычисления без явного указания точности, SQL Server может применить правила неявного приведения типов. Существует несколько ключевых факторов:

  1. Ограничения платформы: Официально 1С:Предприятие гарантирует точность арифметических операций в запросах только до 8 знаков после запятой. Если вам нужно больше (например, 9 или 10), стандартные механизмы могут подвести.
  2. Внутренние типы СУБД: При выполнении группировки SQL Server создает внутренний буфер для накопления суммы. Если тип данных не определен жестко, может использоваться стандартный NUMERIC с фиксированной шкалой, которая меньше вашей.
  3. Виртуальные таблицы: Использование виртуальных таблиц, таких как Обороты, усложняет ситуацию, так как платформа сама строит сложный подзапрос для получения итогов, и на каждом этапе вложенности может происходить потеря точности.

Основное решение: использование функции ВЫРАЗИТЬ

Проанализируем наиболее эффективный способ решения проблемы — использование функции ВЫРАЗИТЬ. Эта функция заставляет платформу 1С добавить в SQL-код явную команду CAST или CONVERT, указывая СУБД точную разрядность числа.

Чтобы исправить ошибку округления, нам нужно обернуть суммируемое поле в ВЫРАЗИТЬ прямо внутри агрегатной функции:


ВЫБРАТЬ
    ТестОбороты.Товар КАК Товар,
    СУММА(ВЫРАЗИТЬ(ТестОбороты.КоличествоОборот КАК ЧИСЛО(22, 9))) КАК КоличествоОборот
ИЗ
    РегистрНакопления.Тест.Обороты КАК ТестОбороты
СГРУППИРОВАТЬ ПО
    ТестОбороты.Товар

Важный нюанс: Указывайте размерность точно такую же, как у ресурса в метаданных (в нашем примере это ЧИСЛО(22, 9)). Это гарантирует, что SQL Server выделит достаточно места для хранения промежуточных сумм без потери знаков.

Альтернативный метод: Масштабирование (Умножение и деление)

В некоторых случаях, особенно при работе со старыми версиями платформы или специфическими настройками СУБД, метод с ВЫРАЗИТЬ может не давать 100% гарантии (например, при очень сложных вложенных расчетах). Тогда мы можем применить метод масштабирования.

Рассмотрим этот метод по шагам:

  1. Умножаем исходное число на коэффициент (например, на 10 000 или 1 000 000) внутри запроса, чтобы превратить дробную часть в целую или значительно уменьшить количество знаков после запятой.
  2. Выполняем суммирование и группировку.
  3. Делим полученный результат обратно на тот же коэффициент.

Пример реализации в коде запроса:


ВЫБРАТЬ
    ТестОбороты.Товар КАК Товар,
    СУММА(ТестОбороты.КоличествоОборот * 1000000) / 1000000 КАК КоличествоОборот
ИЗ
    РегистрНакопления.Тест.Обороты КАК ТестОбороты
СГРУППИРОВАТЬ ПО
    ТестОбороты.Товар

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

Работа с временными таблицами

Если ваш запрос сложный и включает использование временных таблиц, крайне важно следить за типами данных на каждом этапе. Если вы помещаете данные во временную таблицу без ВЫРАЗИТЬ, колонка в tempdb на стороне SQL может создаться с точностью по умолчанию (обычно до 6 знаков).

Посмотрим на правильный пример создания временной таблицы:


ВЫБРАТЬ
    ВЫРАЗИТЬ(ТестОбороты.КоличествоОборот КАК ЧИСЛО(22, 9)) КАК Количество,
    ТестОбороты.Товар КАК Товар
ПОМЕСТИТЬ ВТ_Данные
ИЗ
    РегистрНакопления.Тест.Обороты КАК ТестОбороты;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    СУММА(ВТ_Данные.Количество) КАК ИтоговоеКоличество,
    ВТ_Данные.Товар
ИЗ
    ВТ_Данные КАК ВТ_Данные
СГРУППИРОВАТЬ ПО
    ВТ_Данные.Товар

Почему в файловой версии всё работает иначе?

Многие разработчики замечают, что в файловой базе данных (локальной) проблема не воспроизводится. Это объясняется тем, что файловый движок 1С использует собственные алгоритмы обработки чисел, которые более лояльны к высокой точности и не всегда следуют строгим правилам типизации ANSI SQL. Однако ориентация на файловую версию при разработке — плохая практика, так как большинство рабочих систем функционирует на SQL Server, где типизация критична.

Рекомендации для сохранения точности

Подведем итоги и сформируем правила, которые помогут избежать ошибок в будущем:

Соблюдение этих простых правил позволит вам избежать расхождений в отчетах и обеспечит корректную работу алгоритмов расчета в любой среде эксплуатации.

← На главную