Как округлить число в запросе на языке 1С
Содержание
Функции Окр() в языке запросов 1С нет — она есть только во встроенном языке, то есть в модулях. Если вписать Окр(Сумма, 2) прямо в текст запроса, конструктор подчеркнёт это как ошибку, и запрос не выполнится. В запросе округляют оператором ВЫРАЗИТЬ с типом ЧИСЛО: например, ВЫРАЗИТЬ(Сумма КАК ЧИСЛО(15, 2)).
На этом чаще всего спотыкаются: язык запросов и встроенный язык — разные языки с разным набором функций. То, что привычно в модуле (Окр, Цел, НачалоМесяца), в запросе называется иначе или делается через ВЫРАЗИТЬ, НАЧАЛОПЕРИОДА и РАЗНОСТЬДАТ.
Ниже — как округлять суммы до копеек, как получить целую часть, как округлять даты, почему итог по строкам расходится с итогом по сумме на копейку и какие ошибки точности возникают у типа Число.
Чем округляют в запросе: оператор ВЫРАЗИТЬ
Основной инструмент — ВЫРАЗИТЬ(Выражение КАК ЧИСЛО(Разрядность, Точность)):
- Разрядность — общее число значащих цифр (и до, и после запятой).
- Точность — сколько знаков оставить после запятой.
Сокращая точность, платформа округляет значение. Пример — поделить сумму на три равные части и округлить до копеек:
ВЫБРАТЬ
ВЫРАЗИТЬ(Продажи.СуммаДокумента / 3 КАК ЧИСЛО(15, 2)) КАК Доля
ИЗ
Документ.РеализацияТоваровУслуг КАК Продажи
Если СуммаДокумента = 100, то 100 / 3 = 33,3333…, а ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) даёт 33,33.
Разрядность берите с запасом под целую часть. ВЫРАЗИТЬ(123456.78 КАК ЧИСЛО(5, 2)) не помещается в пять знаков — целая часть 123456 длиннее, и запрос вернёт ошибку переполнения. Для денежных сумм безопасно ЧИСЛО(15, 2).
Чем это отличается от округления в модуле, удобно держать в одной таблице:
| Задача | Встроенный язык (модуль) | Язык запросов |
|---|---|---|
| Округлить до копеек | Окр(Сумма, 2) | ВЫРАЗИТЬ(Сумма КАК ЧИСЛО(15, 2)) |
| Целая часть | Цел(Число) | ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 0)) (но округляет) |
| Округлить дату до месяца | НачалоМесяца(Дата) | НАЧАЛОПЕРИОДА(Дата, МЕСЯЦ) |
| Число дней между датами | — | РАЗНОСТЬДАТ(Д1, Д2, ДЕНЬ) |
Режим округления «0,5 вверх» или «0,5 вниз» у ВЫРАЗИТЬ в редких случаях зависит от СУБД (файловая база, MS SQL, PostgreSQL). Если расхождение в копейку критично — округляйте в модуле функцией Окр с явным третьим параметром, а не в запросе.
Если округлять в модуле: функция Окр и её режимы
Когда данные уже выгружены в таблицу значений или обрабатываются построчно, округляют во встроенном языке. Здесь живёт та самая Окр, которой нет в запросе:
Окр(1234.5678, 2); // 1234.57 — до двух знаков
Окр(1234.5, -2); // 1200 — до сотен (отрицательная разрядность)
- Первый параметр — число.
- Второй — разрядность: положительная округляет до знаков после запятой, отрицательная — до десятков, сотен, тысяч.
- Третий — режим:
РежимОкругления.Окр15как20(0,5округляется вверх) илиРежимОкругления.Окр15как10(0,5отбрасывается вниз).
Режим задавайте явно. От значения по умолчанию лучше не зависеть: в расчётах налогов и зарплаты разница в сторону округления 0,5 — это реальные копейки в проводках. Окр(2.5, 0, РежимОкругления.Окр15как20) вернёт 3, а с режимом Окр15как10 — 2.
Эта функция и есть «основной инструмент» округления у разработчика. В запросе её заменяет ВЫРАЗИТЬ, но вся логика с режимами и отрицательной разрядностью доступна только в модуле.
Как получить целую часть числа без функции Цел
Функции Цел() в запросе тоже нет. Целую часть получают тем же ВЫРАЗИТЬ с нулевой точностью:
ВЫРАЗИТЬ(Товары.Количество КАК ЧИСЛО(15, 0))
Здесь важная ловушка: ЧИСЛО(15, 0) округляет, а не отбрасывает дробную часть. ВЫРАЗИТЬ(2.7 КАК ЧИСЛО(15, 0)) даст 3, а не 2. Если нужно именно отбросить дробь (как делает Цел в модуле), в запросе чистого способа нет — для положительных чисел применяют приём со сдвигом:
ВЫРАЗИТЬ(Товары.Количество - 0.5 КАК ЧИСЛО(15, 0))
Он работает для положительных значений и приблизительно: на точной половине (ровно x,5) результат непредсказуем. Если нужна честная целая часть с учётом знака — считайте её во встроенном языке функцией Цел, а не в запросе. Во встроенном языке Цел(1234.99) вернёт 1234, а Цел(-1234.99) — -1234 (отбрасывание к нулю).
Когда целая часть нужна только для группировки (например, разложить по сотням или тысячам), удобнее поделить и округлить вниз арифметикой, а не искать аналог Цел.
Округление дат: начало периода и разность дат
«Округлить» дату — это привести её к началу или концу дня, месяца, квартала, года. В запросе для этого есть НАЧАЛОПЕРИОДА и КОНЕЦПЕРИОДА:
ВЫБРАТЬ
НАЧАЛОПЕРИОДА(Док.Дата, МЕСЯЦ) КАК НачалоМесяца,
КОНЕЦПЕРИОДА(Док.Дата, КВАРТАЛ) КАК КонецКвартала,
НАЧАЛОПЕРИОДА(Док.Дата, ДЕНЬ) КАК НачалоДня
ИЗ
Документ.РеализацияТоваровУслуг КАК Док
Доступные периоды: ДЕНЬ, НЕДЕЛЯ, МЕСЯЦ, КВАРТАЛ, ГОД, а для времени — ЧАС, МИНУТА, СЕКУНДА. НАЧАЛОПЕРИОДА(Дата, ДЕНЬ) обнуляет время и оставляет полночь — удобно, когда в поле записана дата со временем, а сравнивать нужно только по дню.
Разницу между датами в запросе считает РАЗНОСТЬДАТ (а не выдуманное «ПолучитьПериодМежду», которое иногда встречается в примерах):
РАЗНОСТЬДАТ(ДатаНачала, ДатаОкончания, ДЕНЬ)
Третий параметр задаёт единицу: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, МЕСЯЦ, КВАРТАЛ, ГОД. Результат — целое число периодов. Например, РАЗНОСТЬДАТ в днях между 01.01 и 31.01 вернёт 30.
РАЗНОСТЬДАТ(..., МЕСЯЦ) считает границы календарных месяцев, а не «ровно 30 дней». Между 31 января и 1 февраля функция вернёт 1 месяц, хотя прошли всего сутки. Для расчёта стажа или срока это нужно учитывать.
Тип Число, разрядность и потеря точности
У типа Число в запросе две характеристики — разрядность (сколько всего цифр) и точность (сколько после запятой). Проблемы точности возникают в двух местах.
Деление даёт длинный хвост. Результат деления в запросе может нести много знаков после запятой. Если потом сравнивать такое число на равенство, сравнение не сработает: 10 / 3 * 3 не всегда ровно 10. Лечение — приводить результат к нужной точности ВЫРАЗИТЬ перед сравнением или группировкой.
Та же причина ломает соединения и группировки по вычисляемому числу. Когда в условии соединения или в СГРУППИРОВАТЬ ПО стоит результат деления, строки с «почти равными» значениями не склеятся — платформа видит 33,333333 и 33,333334 как разные. Приводите ключ группировки к фиксированной точности заранее.
Переполнение разрядности. Если задать слишком маленькую разрядность, значение в неё не влезет. ВЫРАЗИТЬ(СуммаМиллионы КАК ЧИСЛО(6, 2)) на сумме 1 234 567,89 вернёт ошибку. Поэтому для денег берут ЧИСЛО(15, 2), для количеств с дробями — ЧИСЛО(15, 3).
Если поле может быть NULL (например, после левого соединения), оберните его в ЕСТЬNULL(Поле, 0) до округления. ВЫРАЗИТЬ(NULL КАК ЧИСЛО(15, 2)) вернёт NULL, и дальше по строке посыпятся пустые итоги.
Короткий чек перед тем, как доверять числу из запроса:
- деление и проценты — обязательно через
ВЫРАЗИТЬдо нужной точности; - суммы для сравнения на равенство — к одной точности с обеих сторон;
- возможные
NULL— закрытыЕСТЬNULL; - разрядность с запасом под самую крупную ожидаемую сумму.
Округлять до суммирования или после: разница в копейках
Самая коварная тема. СУММА — агрегатная функция, и порядок «сначала округлить, потом сложить» против «сначала сложить, потом округлить» даёт разный результат.
// Вариант А: округляем каждую строку, затем суммируем
СУММА(ВЫРАЗИТЬ(Строки.Цена * Строки.Количество КАК ЧИСЛО(15, 2)))
// Вариант Б: суммируем, затем округляем итог
ВЫРАЗИТЬ(СУММА(Строки.Цена * Строки.Количество) КАК ЧИСЛО(15, 2))
Пусть в документе три строки по 10,005:
- Вариант А: каждая строка округляется до
10,01, сумма трёх —30,03. - Вариант Б: сумма
30,015округляется до30,02.
Расхождение — копейка. В печатных формах и проводках это всплывает постоянно.
То же касается СРЕДНЕЕ: среднее почти всегда получается дробным, и округлять его нужно после агрегации, а не внутри. Округлив каждое слагаемое до среднего, вы исказите сам результат.
Правило: для первички округляйте построчно (как в документе) и только потом считайте итог — тогда итог совпадёт с суммой строк, которую видит пользователь. Для аналитических отчётов, где строки не показывают, допустимо округлять итог.
Пересчёт валюты и округление до копеек
При пересчёте валютных сумм в рубли округление до копеек обязательно — иначе в проводках появятся доли копейки. Формула простая: сумма × курс ÷ кратность, и всё это через ВЫРАЗИТЬ:
ВЫБРАТЬ
ВЫРАЗИТЬ(Строки.СуммаВалюта * Курсы.Курс / Курсы.Кратность
КАК ЧИСЛО(15, 2)) КАК СуммаРуб
ИЗ
...
- Курс — сколько рублей за единицу (или за кратность) валюты.
- Кратность — за сколько единиц валюты указан курс. Для большинства валют
1, но для валют с мелким номиналом курс дают за100единиц — тогда делить нужно на100.
Пример: 100 USD по курсу 92,3456 и кратности 1 → 100 × 92,3456 = 9234,56 ₽. Округление до копеек ничего не меняет, сумма уже ровная.
Для валюты с кратностью 100 формула учитывает делитель. Пусть курс дан как 62,50 за 100 единиц, тогда 1000 × 62,50 / 100 = 625,00 ₽. Если забыть про кратность и умножить напрямую, сумма вырастет в сто раз — частая причина завышенных валютных остатков в отчётах.
Отдельно — выделение НДС. НДС из суммы с налогом по ставке 20% считают как сумма × 20 ÷ 120, и результат округляют до копеек:
ВЫРАЗИТЬ(Строки.Сумма * 20 / 120 КАК ЧИСЛО(15, 2))
Для 1234,56 это 1234,56 × 20 / 120 = 205,76. Если НДС считать по каждой строке и округлять построчно, итоговый НДС по документу должен сходиться с суммой построчных — поэтому порядок округления из предыдущего раздела здесь так же важен, как в первичке.
Курсовые разницы и пересчёт на дату — отдельная тема учёта. В запросе вы только корректно округляете, а правило пересчёта (на дату документа, оплаты или отчётности) определяет учётная политика.
Частые ошибки при округлении в запросе
- Пишут
ОкриЦелв тексте запроса. Их там нет — толькоВЫРАЗИТЬ.Окр/Целоставляйте для модулей. - Ждут от
ЧИСЛО(15, 0)отбрасывания дроби. Оно округляет.2,7станет3, а не2. - Сравнивают неокруглённые дроби на равенство. После деления у числа длинный хвост, условие
=не срабатывает. Приводите обе стороны к одной точности. - Маленькая разрядность.
ЧИСЛО(6, 2)на крупной сумме — ошибка переполнения. Берите15знаков под деньги. - Забывают про
NULL. После соединения пустое поле «съедает» всю строку. Оборачивайте вЕСТЬNULLдо округления. - Округляют итог там, где нужен построчный расчёт. Копеечные расхождения между суммой строк и итогом документа — почти всегда отсюда.
Частые вопросы
Почему `Окр()` не работает в запросе?
ВЫРАЗИТЬ(Выражение КАК ЧИСЛО(разрядность, точность)).Как в запросе округлить до целого в большую сторону?
Окр с режимом.Как округлить до десятков или сотен?
Окр(Число, -1) для десятков, Окр(Число, -2) для сотен (отрицательная разрядность). В запросе ВЫРАЗИТЬ отрицательную точность не принимает, поэтому такое округление делают во встроенном языке.Чем `НАЧАЛОПЕРИОДА` отличается от округления числа?
НАЧАЛОПЕРИОДА работает с датами: приводит дату к началу дня, месяца, квартала или года. К числам она неприменима — для чисел только ВЫРАЗИТЬ.