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

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

При разработке сложных отчетов или механизмов расчета в 1С программисты часто сталкиваются с необходимостью округлять числовые показатели непосредственно в запросе. Стандартным инструментом для этого является функция ВЫРАЗИТЬ(). Однако у неё есть существенное ограничение: количество знаков после запятой должно быть жестко задано константой. Попытка передать туда параметр или значение из другого поля таблицы (например, индивидуальную точность округления для каждого контрагента или номенклатуры) приводит к ошибке конфигуратора "Ожидается константа".

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

Метод 1: Математическая эмуляция через умножение и деление

Самый гибкий способ, позволяющий округлять числа до любой разрядности, заданной в данных, — это использование математической формулы. Суть метода заключается в том, чтобы «поднять» дробную часть числа в целую часть, округлить результат до целого и вернуть его обратно делением.

Рассмотрим ситуацию, когда в нашей базе хранится коэффициент округления (например, для 2 знаков это 100, для 3 знаков — 1000). Проанализируем базовый алгоритм:

  1. Умножаем исходное число на коэффициент (10 в степени нужной точности).
  2. Применяем функцию ВЫРАЗИТЬ() с приведением к типу ЧИСЛО(15, 0). Это обеспечит арифметическое округление.
  3. Делим полученный результат обратно на тот же коэффициент.

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


ВЫБРАТЬ
    МояТаблица.Номенклатура,
    ВЫРАЗИТЬ(МояТаблица.Цена * ВТПараметры.Коэффициент КАК ЧИСЛО(15, 0)) / ВТПараметры.Коэффициент КАК ЦенаОкругленная
ИЗ
    Справочник.Номенклатура КАК МояТаблица
ЛЕВОЕ СОЕДИНЕНИЕ ВТПараметры КАК ВТПараметры
    ПО МояТаблица.Ссылка = ВТПараметры.Ссылка

Важное замечание по правилам округления: стандартная функция ВЫРАЗИТЬ() в языке запросов 1С выполняет арифметическое округление. Это значит, что 0.5 округляется до 1, а 0.4 — до 0. Поэтому добавлять к числу 0.5 (как это часто делается в классической математике программирования для округления вниз) в данном случае не требуется, если ваша цель — стандартное поведение.

Метод 2: Решение проблемы переполнения поля (Overflow)

Проанализируем ситуацию, с которой часто сталкиваются разработчики при реализации первого метода. При умножении поля на поле SQL-сервер может выдать ошибку "Переполнение поля". Выясним причину: когда СУБД перемножает два числа с фиксированной точностью (например, 15.2 и 10.0), она резервирует под промежуточный результат тип данных, разрядность которого равна сумме разрядностей множителей. Если это значение превышает максимально допустимые 38 знаков, запрос аварийно завершается.

Чтобы избежать этой ситуации, мы должны принудительно ограничить разрядность множителей еще до выполнения операции умножения. Проверим, как работает «двойное выражение» типов:


ВЫБРАТЬ
    // Сначала выражаем коэффициент как число с конкретной точностью, 
    // чтобы SQL не раздувал тип данных промежуточного результата
    ВЫРАЗИТЬ(МояТаблица.МоёЧисло * ВЫРАЗИТЬ(ВТПараметры.СтепеньОкругления КАК ЧИСЛО(15, 0)) КАК ЧИСЛО(22, 0)) 
    / ВТПараметры.СтепеньОкругления КАК Результат
ИЗ
    МояТаблица КАК МояТаблица

Как показала практика, явное приведение коэффициента к типу ЧИСЛО(15, 0) внутри выражения позволяет СУБД построить корректный план запроса без избыточного резервирования памяти под огромные числа.

Метод 3: Использование конструкции ВЫБОР КОГДА (статический набор вариантов)

Если в вашей системе количество вариантов разрядности ограничено (например, вы знаете, что округление бывает только до 0, 2 или 3 знаков), рассмотрим более наглядный, хотя и более громоздкий способ. Этот метод «красивее» с точки зрения чтения кода, так как использует стандартные возможности ВЫРАЗИТЬ() для каждого случая отдельно.

Разберем пример реализации:


ВЫБРАТЬ
    ВТ.Товар,
    ВЫБОР
        КОГДА ВТ.Точность = 0 ТОГДА ВЫРАЗИТЬ(ВТ.Сумма КАК ЧИСЛО(15, 0))
        КОГДА ВТ.Точность = 1 ТОГДА ВЫРАЗИТЬ(ВТ.Сумма КАК ЧИСЛО(15, 1))
        КОГДА ВТ.Точность = 2 ТОГДА ВЫРАЗИТЬ(ВТ.Сумма КАК ЧИСЛО(15, 2))
        ИНАЧЕ ВЫРАЗИТЬ(ВТ.Сумма КАК ЧИСЛО(15, 3))
    КОНЕЦ КАК СуммаСУчетомТочности
ИЗ
    ВТДанные КАК ВТ

Этот подход исключает любые проблемы с переполнением и математическими погрешностями деления, так как СУБД напрямую выполняет приведение типов (CAST). Основной недостаток — необходимость перечислять все возможные варианты точности. Если точность может быть равна, скажем, 10 знакам, запрос станет трудночитаемым.

Метод 4: Округление «всегда вверх» или «всегда вниз»

Иногда бизнес-логика требует не арифметического округления, а, например, округления до целого всегда в большую сторону (ценообразование в пользу компании). Поскольку функций CEIL (потолок) или FLOOR (пол) в языке запросов 1С нет, мы можем эмулировать их с помощью небольших добавок.

Округление всегда вверх: добавьте к числу значение 0.4999999999 (количество девяток зависит от требуемой точности исходных данных) и примените ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 0)). Если исходное число было 1.01, оно превратится в 1.509... и округлится до 2. Если было 1.00, оно превратится в 1.499... и останется 1.

Округление вниз (отсечение): аналогично, вычтите 0.5 или используйте ВЫРАЗИТЬ с меньшей разрядностью после предварительного вычитания малой дельты.

Рекомендации по проектированию метаданных

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

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

← На главную