Округление в языке запросов 1С: функции, обходные приёмы и подводные камни СУБД
Содержание
Чтобы округлить число в запросе 1С, используйте функцию ОКР(Число, Разрядность): ОКР(Цена * 1.2, 2) вернёт сумму до копеек. Целую часть даёт ЦЕЛ(Число). Обе функции появились в языке запросов в платформе 8.3.20. На более ранних платформах число в запросе округляют приведением типа: ВЫРАЗИТЬ(Сумма КАК ЧИСЛО(15, 2)).
Готовых ОКРВВЕРХ и ОКРВНИЗ в языке запросов 1С нет. Округление вверх и вниз собирают из ЦЕЛ и конструкции ВЫБОР. Ниже — рабочие выражения для каждого случая и то, где округление в запросе даёт неожиданный результат.
Функция ОКР: обычное округление числа в запросе
ОКР округляет по школьным правилам: пятёрка уходит вверх по модулю. Синтаксис:
ОКР(<Выражение>, <Разрядность>)
- 🔢
<Выражение>— поле, параметр или арифметика над ними; - 🎯
<Разрядность>— сколько знаков оставить после запятой.0— до целых,2— до копеек.
Пример: цена с наценкой 17,5 % до копеек.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Товар,
ОКР(Цены.Цена * 1.175, 2) КАК ЦенаСНаценкой
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Дата, ) КАК Цены
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
ПО Цены.Номенклатура = Номенклатура.Ссылка
Проверим на числах. Цена 100,30: 100,30 × 1,175 = 117,8525. ОКР(…, 2) даёт 117,85. Цена 99,90: 99,90 × 1,175 = 117,3825, результат 117,38.
Регистр и поля в примере условные — подставьте свои. Важен приём: множьте и округляйте в одном выражении, тогда в выборку уходит уже готовая сумма.
Округлять до десятков или сотен удобнее через деление: ОКР(Сумма / 100, 0) * 100. Из 1 249 получится 1 200, из 1 250 — 1 300. Так выражение читается однозначно и не зависит от того, примет ли СУБД отрицательную разрядность.
Если платформа старше 8.3.20
На платформе с номером ниже 8.3.20 ОКР в запросе вызовет ошибку синтаксиса. То же бывает на новой платформе, если у конфигурации включён режим совместимости с более ранней версией. Проверить его можно в конфигураторе: свойства корня конфигурации → Режим совместимости.
Менять режим совместимости ради одной функции в типовой конфигурации не стоит. Для таких баз есть приём с ВЫРАЗИТЬ, он разобран ниже.
ЦЕЛ, округление вверх и вниз в запросе 1С
ЦЕЛ(Число) отбрасывает дробную часть. Это не округление вниз, а усечение к нулю:
| Число | ЦЕЛ | Округление вниз | Округление вверх |
|---|---|---|---|
| 7,3 | 7 | 7 | 8 |
| 7,0 | 7 | 7 | 7 |
| −7,3 | −7 | −8 | −7 |
Для положительных чисел ЦЕЛ и округление вниз совпадают. Расходятся они только на отрицательных. Это важно для остатков, сторно и корректировок, где минус — обычное дело.
Округление вверх (аналог ОКРВВЕРХ)
Типичная задача — сколько упаковок нужно, чтобы покрыть потребность. 25 штук при упаковке по 12 — это 3 упаковки, а не 2.
ВЫБОР
КОГДА Потребность / Кратность = ЦЕЛ(Потребность / Кратность)
ТОГДА Потребность / Кратность
ИНАЧЕ ЦЕЛ(Потребность / Кратность) + 1
КОНЕЦ КАК Упаковок
Проверка: 25 / 12 = 2,083…; ЦЕЛ = 2; дробь есть, значит 2 + 1 = 3. При потребности 24 частное ровно 2, ответ 2.
Выражение рассчитано на положительные числа. Если в данных бывает минус, добавьте ветку:
ВЫБОР
КОГДА Х = ЦЕЛ(Х) ТОГДА Х
КОГДА Х > 0 ТОГДА ЦЕЛ(Х) + 1
ИНАЧЕ ЦЕЛ(Х)
КОНЕЦ
Здесь Х — ваше выражение. Для −7,3 сработает третья ветка: ЦЕЛ даст −7, это и есть округление вверх.
Округление вниз (аналог ОКРВНИЗ)
ВЫБОР
КОГДА Х = ЦЕЛ(Х) ТОГДА Х
КОГДА Х < 0 ТОГДА ЦЕЛ(Х) - 1
ИНАЧЕ ЦЕЛ(Х)
КОНЕЦ
Для 7,3 ответ 7, для −7,3 ответ −8.
Вверх или вниз до копеек
Тот же приём работает с любой разрядностью. Умножьте на 100, округлите до целого, разделите обратно. Округление вниз до копеек для положительной суммы:
ЦЕЛ(Сумма * 100) / 100
Сумма 152,4791 → 15 247,91 → 15 247 → 152,47.
Округление в запросе на старых платформах: приём с ВЫРАЗИТЬ
До 8.3.20 единственный штатный способ округлить в запросе — привести число к типу с меньшей точностью:
ВЫРАЗИТЬ(Сумма * 1.2 КАК ЧИСЛО(15, 2))
Лишние знаки после запятой отсекаются с округлением. 1 000,005 превратится в 1 000,01, а 99,994 — в 99,99. Вторым параметром ЧИСЛО(15, 2) задают число знаков, первым — общую длину.
Длину берите с запасом. Если результат не помещается в ЧИСЛО(15, 2), то есть в нём больше 13 цифр до запятой, запрос завершится ошибкой переполнения. Для оборотов крупной компании за год ставьте ЧИСЛО(20, 2) или больше.
Целая часть через ВЫРАЗИТЬ
ВЫРАЗИТЬ(Х КАК ЧИСЛО(15, 0)) даёт ближайшее целое, а не целую часть. Чтобы получить усечение, на старых платформах вычитали почти половину:
ВЫРАЗИТЬ(Х - 0.4999999 КАК ЧИСЛО(15, 0))
Для 7,3: 6,8000001 → 7. Для 7,9: 7,4000001 → 7. Для 7,0: 6,5000001 → 7.
Приём рабочий, но с оговорками:
- ❗ подходит только для положительных чисел;
- ❗ точность ограничена числом девяток: значение 7,99999995 даст 8, а не 7;
- ❗ выражение плохо читается, через год никто не вспомнит, откуда 0,4999999.
Округление вверх на старой платформе — тот же приём с плюсом: ВЫРАЗИТЬ(Х + 0.4999999 КАК ЧИСЛО(15, 0)). Для 2,083 получится 3, для ровного 2 — тоже 2.
Если платформа позволяет, переходите на ОКР и ЦЕЛ: они честно обрабатывают и отрицательные значения, и любое число знаков.
Как СУБД считает числа и почему результат «плывёт»
Запрос 1С не выполняется сам по себе. Платформа переводит его в SQL, а считает уже СУБД: файловая база, MS SQL Server или PostgreSQL. Отсюда несколько эффектов, которых нет во встроенном языке.
Точность деления
Результат деления в запросе имеет ограниченное число знаков после запятой. Сколько именно — зависит от СУБД и типов операндов. Поэтому 1 / 3 * 3 может дать не ровно 1, а 0,999999…
Правило простое: сначала умножайте, потом делите.
| Выражение | Что получится |
|---|---|
Сумма / Количество * 100 | промежуточное частное уже усечено, ошибка множится на 100 |
Сумма * 100 / Количество | деление последним, потеря точности минимальна |
Если результат деления дальше участвует в расчётах, оберните его в ОКР до нужной точности сразу. Так все последующие операции работают с предсказуемым числом.
Деление на ноль
Деление на поле, где встречается 0, ломает весь запрос, а не одну строку. Защищайтесь заранее:
ВЫБОР
КОГДА Количество = 0 ТОГДА 0
ИНАЧЕ ОКР(Сумма / Количество, 2)
КОНЕЦ КАК Цена
NULL в арифметике
Любая операция с NULL даёт NULL. После левого соединения пустое поле превратит ОКР(Цена * Количество, 2) в пустоту. Оберните поле в ЕСТЬNULL(Цены.Цена, 0) до умножения.
Одна база — разные СУБД
Если база переезжает с файлового варианта на MS SQL или PostgreSQL, сверьте контрольные отчёты. Целые суммы совпадут. Расхождения, если появятся, будут в последних знаках после длинных делений. Чем раньше в выражении стоит ОКР, тем меньше таких сюрпризов.
Сочетание функций округления в одном выражении
Округление до заданного шага
Цены в рознице часто ставят кратными 5 или 10 рублям. Общая формула для шага &Шаг:
ОКР(Цена / &Шаг, 0) * &Шаг
При шаге 5: 147,30 / 5 = 29,46 → 29 → 145. Цена 148,00: 29,6 → 30 → 150.
Нужна «психологическая» цена вида 149, 199, 249? Округлите вверх до 50 и вычтите рубль. Цена 173: 173 / 50 = 3,46, вверх → 4, 4 × 50 = 200, минус 1 = 199.
Округлить до итога или после
Порядок округления и группировки меняет сумму. Возьмём три строки по 33,333:
| Подход | Выражение | Итог |
|---|---|---|
| Сначала округлить, потом сложить | СУММА(ОКР(Сумма, 2)) | 33,33 × 3 = 99,99 |
| Сначала сложить, потом округлить | ОКР(СУММА(Сумма), 2) | 99,999 → 100,00 |
Ни один вариант не ошибочен. Выбор зависит от того, с чем сверяетесь. Документ хранит суммы по строкам уже округлёнными, поэтому для сверки с документами нужен первый вариант. Для аналитики по «сырым» коэффициентам — второй.
Копейка на последнюю строку
При распределении суммы пропорционально базе округление каждой доли почти всегда даёт расхождение в копейку-две. Запросом это не лечится: «хвост» нужно отдать одной строке, обычно самой крупной. Это делают во встроенном языке после выполнения запроса: считают разницу между исходной суммой и суммой округлённых долей и прибавляют её к строке с наибольшей долей.
Пример: 100 рублей на три подразделения поровну. Доли 33,33 + 33,33 + 33,33 = 99,99. Копейка уходит в первую строку: 33,34.
Сравнение способов округлить число в запросе
| Задача | Платформа 8.3.20 и новее | Платформа ниже 8.3.20 | Отрицательные числа |
|---|---|---|---|
| Математическое округление до N знаков | ОКР(Х, N) | ВЫРАЗИТЬ(Х КАК ЧИСЛО(15, N)) | корректно в обоих |
| Целая часть (усечение) | ЦЕЛ(Х) | ВЫРАЗИТЬ(Х - 0.4999999 КАК ЧИСЛО(15, 0)) | старый приём — нет |
| Округление вверх | ВЫБОР + ЦЕЛ | ВЫРАЗИТЬ(Х + 0.4999999 …) | нужна ветка для минуса |
| Округление вниз | ВЫБОР + ЦЕЛ | старый приём для усечения | нужна ветка для минуса |
| До шага (5, 10, 100) | ОКР(Х / Шаг, 0) * Шаг | ВЫРАЗИТЬ(Х / Шаг КАК ЧИСЛО(15, 0)) * Шаг | корректно |
Запрос или встроенный язык: где округлять
Во встроенном языке у функции Окр есть третий параметр — режим округления. РежимОкругления.Окр15как20 округляет пятёрку вверх, РежимОкругления.Окр15как10 — вниз. В запросе такого выбора нет.
Округляйте в запросе, когда:
- 📊 по округлённому значению идёт отбор, группировка или соединение;
- 🧮 выборка большая, и пересчитывать её циклом дорого;
- 📄 результат запроса сразу уходит в компоновку данных как ресурс.
Округляйте в коде, когда:
- 🔁 нужен режим, где пятёрка уходит вниз;
- 💰 надо разнести копейки остатка;
- 🧩 правило округления зависит от настроек, которые проще проверить в коде, чем в
ВЫБОР.
Отбор по округлённому значению — частый повод округлить именно в запросе. Например, ГДЕ ОКР(Остаток, 3) <> 0 отсекает «пыль» вида 0,0000001 в регистрах с дробным количеством. Без округления такие строки попадут в отчёт как ненулевые остатки.