Числовой результат запроса в 1С: приведение типа, округление, NULL и деление
Содержание
Чтобы выразить результат запроса 1С как число нужной разрядности, оберните поле в ВЫРАЗИТЬ(Поле КАК ЧИСЛО(15, 2)): 15 — общая длина, 2 — знаки после запятой. Конструкция округляет значение, приводит поле составного типа к числу и делает тип колонки предсказуемым. Строку в число она не превращает — для этого есть отдельные приёмы.
Типичная ситуация: программист делит выручку на количество прямо в запросе, а в таблице значений получает цену с десятком знаков после запятой. Или собирает сумму по левому соединению и видит пустую ячейку вместо нуля. Обе проблемы решаются в тексте запроса — разберём синтаксис, агрегаты, деление, чтение числа в коде и ошибки, на которых спотыкаются чаще всего.
Синтаксис ВЫРАЗИТЬ КАК ЧИСЛО и получение одного числа
Общая форма такая:
ВЫРАЗИТЬ(<Выражение> КАК ЧИСЛО(<Длина>, <Точность>))
Что умеет ВЫРАЗИТЬ для чисел:
- 🔢 менять разрядность:
ВЫРАЗИТЬ(Цена КАК ЧИСЛО(15, 2))даст два знака после запятой; - 🎯 округлять: при уменьшении точности значение округляется по арифметическим правилам,
ЧИСЛО(15, 0)— до целого; - 🧩 сужать составной тип: если реквизит может хранить и число, и строку, и ссылку,
ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2))вернёт число, а для нечисловых значений —NULL; - 📐 фиксировать тип колонки во временной таблице и в объединении.
Чего ВЫРАЗИТЬ не умеет: преобразовывать строку в число и число в строку. Язык запросов меняет только «размер» внутри одного примитивного типа и сужает составной тип. Для строк с цифрами нужен обходной путь (следующий раздел).
Когда нужен один результат, а не таблица
Если запрос должен вернуть одно число — итог, максимум, последний курс, — есть два варианта.
- Агрегат без группировки.
ВЫБРАТЬ СУММА(...)безСГРУППИРОВАТЬ ПОвсегда возвращает ровно одну строку, даже если подходящих записей нет. ВЫБРАТЬ ПЕРВЫЕ 1с сортировкой. Так берут последнее по дате значение:
ВЫБРАТЬ ПЕРВЫЕ 1
ВЫРАЗИТЬ(Т.Курс КАК ЧИСЛО(15, 4)) КАК Курс
ИЗ
РегистрСведений.КурсыВалют КАК Т
ГДЕ
Т.Валюта = &Валюта
И Т.Период <= &Дата
УПОРЯДОЧИТЬ ПО
Т.Период УБЫВ
Для последнего значения периодического регистра сведений удобнее виртуальная таблица СрезПоследних(&Дата, Валюта = &Валюта). Вариант с ПЕРВЫЕ 1 полезен, когда условие сложнее, чем умеет срез.
Разница вариантов: агрегат без строк вернёт строку с NULL, а ПЕРВЫЕ 1 — пустой результат без строк. Код чтения должен учитывать оба случая.
Строка в число в запросе: обходной путь
Задача встречается часто: код справочника строковый, а нужно сравнить его как число или найти максимальный номер. ВЫРАЗИТЬ(Код КАК ЧИСЛО) здесь не сработает — платформа не преобразует строку в число.
Есть три рабочих подхода.
| Способ | Когда подходит | Минусы |
|---|---|---|
Таблица цифр и ПОДСТРОКА | Строка короткая и фиксированной длины, нужно число прямо в запросе | Громоздкий текст, на каждую позицию — соединение |
| Сравнение строк | Коды одной длины с ведущими нулями | Работает только для сравнения и сортировки, не для арифметики |
| Преобразование в коде | Длина разная, строки «грязные» | Нужен второй проход по результату |
Таблица цифр
Создаём временную таблицу «символ → цифра» и соединяем её с каждой позицией строки. Пример для кода из трёх цифр:
ВЫБРАТЬ "0" КАК Символ, 0 КАК Цифра
ПОМЕСТИТЬ Цифры
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "1", 1
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "2", 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "3", 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "4", 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "5", 5
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "6", 6
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "7", 7
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "8", 8
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ "9", 9
;
ВЫБРАТЬ
Т.Ссылка,
Ц1.Цифра * 100 + Ц2.Цифра * 10 + Ц3.Цифра КАК КодЧислом
ИЗ
Справочник.Склады КАК Т
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Цифры КАК Ц1
ПО ПОДСТРОКА(Т.Код, 1, 1) = Ц1.Символ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Цифры КАК Ц2
ПО ПОДСТРОКА(Т.Код, 2, 1) = Ц2.Символ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Цифры КАК Ц3
ПО ПОДСТРОКА(Т.Код, 3, 1) = Ц3.Символ
Для кода "047" получится 0 × 100 + 4 × 10 + 7 = 47. Побочный эффект полезный: строки, где на любой позиции стоит не цифра, выпадут из результата внутреннего соединения. Если такие строки нужны, замените соединения на левые и оберните цифры в ЕСТЬNULL.
Сравнение без преобразования
Если все коды одинаковой длины и с ведущими нулями (000123, 000981), строковая сортировка совпадает с числовой. Максимальный номер ищут обычным МАКСИМУМ(Т.Код), без перевода в число.
Преобразование после выполнения
Когда формат строк непредсказуем, проще выгрузить результат и перевести значения в коде — это разобрано в разделе про обработку результата.
Агрегатные функции: СУММА, СРЕДНЕЕ, МАКСИМУМ и тип результата
Агрегаты работают с числами напрямую, но у каждого есть особенность.
- ➕
СУММА— если записей нет, вернётNULL, а не 0. Оборачивайте:ЕСТЬNULL(СУММА(Т.Сумма), 0). - 📊
СРЕДНЕЕ— даёт много знаков после запятой. Для вывода приводите:ВЫРАЗИТЬ(СРЕДНЕЕ(Т.Цена) КАК ЧИСЛО(15, 2)). - 🔝
МАКСИМУМиМИНИМУМ— сохраняют тип поля, но на пустом наборе тоже даютNULL. - 🔢
КОЛИЧЕСТВО— единственный агрегат, который на пустом наборе возвращает 0.
Порядок: сначала агрегат, потом ВЫРАЗИТЬ
Пишите ВЫРАЗИТЬ(СУММА(...) КАК ...), а не СУММА(ВЫРАЗИТЬ(...)), когда цель — округлить итог. Во втором случае округляется каждая строка, и сумма округлённых значений может разойтись с округлённой суммой на копейки.
Обратный порядок нужен в одном случае — поле составного типа. СУММА по такому полю невозможна, поэтому сначала приводим к числу, потом суммируем:
ВЫБРАТЬ
ЕСТЬNULL(СУММА(ВЫРАЗИТЬ(З.Значение КАК ЧИСЛО(15, 2))), 0) КАК Итого
ИЗ
РегистрСведений.ДополнительныеСведения КАК З
ГДЕ
З.Свойство = &Свойство
Нечисловые значения превратятся в NULL, а СУММА их пропустит.
Среднее по отношению — частая ловушка
Средняя цена — это не СРЕДНЕЕ(Сумма / Количество), а СУММА(Сумма) / СУММА(Количество). Пример: две продажи, 1 шт. по 100 ₽ и 9 шт. по 200 ₽. СРЕДНЕЕ от цен даст 150 ₽, а реальная средняя — (100 + 1 800) / 10 = 190 ₽.
Вычисляемые поля: деление, проценты, округление
В запросе доступны +, -, *, / и скобки. Сложности почти всегда возникают с делением.
Защита от деления на ноль
Поведение при делении на ноль зависит от СУБД, полагаться на него нельзя. Проверяйте делитель через ВЫБОР:
ВЫБОР
КОГДА Т.Количество = 0 ТОГДА 0
ИНАЧЕ ВЫРАЗИТЬ(Т.Сумма / Т.Количество КАК ЧИСЛО(15, 2))
КОНЕЦ КАК Цена
Округление до целого, вверх и вниз
Функции округления в языке запросов нет, её заменяет ВЫРАЗИТЬ:
| Нужно | Выражение | 3,2 → | 3,7 → |
|---|---|---|---|
| По правилам арифметики | ВЫРАЗИТЬ(Х КАК ЧИСЛО(15, 0)) | 3 | 4 |
| Вниз (для положительных) | ВЫРАЗИТЬ(Х - 0.5 КАК ЧИСЛО(15, 0)) | 3 | 3 |
| Вверх (для положительных) | через ВЫБОР, см. ниже | 4 | 4 |
Округление вверх нужно, например, чтобы посчитать число коробок по 12 штук:
ВЫБОР
КОГДА ВЫРАЗИТЬ(Т.Количество / 12 КАК ЧИСЛО(15, 0)) < Т.Количество / 12
ТОГДА ВЫРАЗИТЬ(Т.Количество / 12 КАК ЧИСЛО(15, 0)) + 1
ИНАЧЕ ВЫРАЗИТЬ(Т.Количество / 12 КАК ЧИСЛО(15, 0))
КОНЕЦ КАК Коробок
Для 30 штук: 30 / 12 = 2,5, округление даёт 3, это не меньше 2,5 — результат 3 коробки. Для 26 штук: 2,17 → 2, это меньше 2,17 — результат 3. Для 24 штук: ровно 2.
Приёмы «вниз» и «вверх» рассчитаны на положительные числа. Для отрицательных (сторно, возвраты) проверьте результат отдельно или разведите ветки через ВЫБОР.
Проценты и NULL после соединения
После ЛЕВОЕ СОЕДИНЕНИЕ любое поле правой таблицы может оказаться NULL, а NULL в арифметике заражает всё выражение: 100 + NULL даёт NULL. Поэтому каждое такое поле оборачивайте: ЕСТЬNULL(Возвраты.Сумма, 0).
Числовые литералы задавайте явно типизированными, если колонка уходит во временную таблицу или в объединение: ВЫРАЗИТЬ(0 КАК ЧИСЛО(15, 2)) КАК Сумма. Так тип колонки задан явно и не зависит от литерала.
Чтение числа из результата запроса в коде
Иногда преобразование удобнее сделать после выполнения: строки «грязные», нужна сложная логика или форматирование для вывода.
Безопасное чтение одного значения
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЕСТЬNULL(СУММА(Т.Сумма), 0) КАК Сумма
|ИЗ
| РегистрНакопления.Продажи КАК Т
|ГДЕ
| Т.Период МЕЖДУ &Начало И &Конец";
Запрос.УстановитьПараметр("Начало", НачалоМесяца(ТекущаяДата()));
Запрос.УстановитьПараметр("Конец", КонецМесяца(ТекущаяДата()));
Итог = 0;
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Итог = Выборка.Сумма;
КонецЕсли;
Начальное Итог = 0 закрывает случай с ПЕРВЫЕ 1, когда строк нет. ЕСТЬNULL в тексте закрывает агрегат на пустом наборе. Имена регистра и ресурса подставьте из своей конфигурации.
Строка в число средствами встроенного языка
Число(Стр)— переводит корректную строку, на нечисловой вызывает исключение. Оборачивайте вПопытка ... Исключение ... КонецПопытки.Новый ОписаниеТипов("Число").ПривестиЗначение(Стр)— не падает, но для некорректной строки вернёт 0, и «мусор» не отличить от настоящего нуля.- В конфигурациях на БСП есть
СтроковыеФункцииКлиентСервер.СтрокаВЧисло(Стр)— возвращаетНеопределено, если строка не число.
Вывод числа без «хвостов»
Для отображения пользователю используйте Формат(Итог, "ЧДЦ=2; ЧГ=0"). При склейке числа со строкой без Формат платформа вставит разделители групп разрядов, и номер 12345 превратится в 12 345.
Как ускорить запросы с числовыми данными
Числа сами по себе не тормозят запрос. Тормозят вычисления в неудачном месте.
- Не приводите поле в условии.
ГДЕ ВЫРАЗИТЬ(Т.Сумма КАК ЧИСЛО(15, 0)) > 1000мешает СУБД использовать индекс. ПишитеГДЕ Т.Сумма > 1000— результат почти тот же, а индекс работает. - Берите готовые итоги. Для регистров накопления используйте виртуальные таблицы
Остатки,Обороты,ОстаткиИОборотывместо суммирования движений вручную. Отборы передавайте в параметры виртуальной таблицы, а не вГДЕ. - Делите после группировки.
СУММА(Сумма) / СУММА(Количество)считается один раз на группу, а деление в каждой строке — миллион раз на миллион строк. - Сужайте составные типы. Для полей вроде
РегистраторприведениеВЫРАЗИТЬ(Т.Регистратор КАК Документ.РеализацияТоваровУслуг)убирает лишние соединения со всеми таблицами документов. Это тот жеВЫРАЗИТЬ, только для ссылок. - Промежуточные расчёты — во временные таблицы. Если одно и то же выражение нужно в трёх местах, посчитайте его один раз через
ПОМЕСТИТЬ.
Пример: отчёт с выручкой, наценкой и долей
Соберём запрос, где сходятся все приёмы: агрегаты, защита от нуля, округление и NULL. Регистр условный — с ресурсами Выручка и Себестоимость по номенклатуре. Подставьте свои объекты.
ВЫБРАТЬ
П.Номенклатура КАК Номенклатура,
СУММА(П.ВыручкаОборот) КАК Выручка,
СУММА(П.СебестоимостьОборот) КАК Себестоимость
ПОМЕСТИТЬ ВТПродажи
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец) КАК П
СГРУППИРОВАТЬ ПО
П.Номенклатура
;
ВЫБРАТЬ
ЕСТЬNULL(СУММА(В.Выручка), 0) КАК ВсегоВыручка
ПОМЕСТИТЬ ВТИтог
ИЗ
ВТПродажи КАК В
;
ВЫБРАТЬ
В.Номенклатура,
ВЫРАЗИТЬ(В.Выручка КАК ЧИСЛО(15, 2)) КАК Выручка,
ВЫБОР
КОГДА В.Себестоимость = 0 ТОГДА 0
ИНАЧЕ ВЫРАЗИТЬ((В.Выручка - В.Себестоимость) / В.Себестоимость * 100 КАК ЧИСЛО(15, 2))
КОНЕЦ КАК НаценкаПроцент,
ВЫБОР
КОГДА И.ВсегоВыручка = 0 ТОГДА 0
ИНАЧЕ ВЫРАЗИТЬ(В.Выручка / И.ВсегоВыручка * 100 КАК ЧИСЛО(15, 2))
КОНЕЦ КАК ДоляПроцент
ИЗ
ВТПродажи КАК В,
ВТИтог КАК И
УПОРЯДОЧИТЬ ПО
Выручка УБЫВ
Проверим на цифрах. Товар А: выручка 120 000 ₽, себестоимость 90 000 ₽. Наценка = (120 000 − 90 000) / 90 000 × 100 = 33,33 %. Если общая выручка 400 000 ₽, доля товара А = 120 000 / 400 000 × 100 = 30,00 %.
Что здесь сделано осознанно:
- ✅ процент считается после группировки, а не построчно;
- ✅ оба деления защищены от нуля;
- ✅ итог для доли посчитан один раз во временной таблице и подключён без условия — в ней одна строка;
- ✅
ВЫРАЗИТЬстоит на финальных значениях, промежуточные суммы не округляются.
Если запрос уходит в СКД, точность вывода можно дополнительно задать в оформлении поля через формат ЧДЦ=2. Но ВЫРАЗИТЬ в запросе всё равно полезен: он задаёт тип ресурса и убирает длинные дроби в расшифровке.