Числовой результат запроса в 1С: приведение типа, округление, NULL и деление

· 10 мин

Содержание

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

Типичная ситуация: программист делит выручку на количество прямо в запросе, а в таблице значений получает цену с десятком знаков после запятой. Или собирает сумму по левому соединению и видит пустую ячейку вместо нуля. Обе проблемы решаются в тексте запроса — разберём синтаксис, агрегаты, деление, чтение числа в коде и ошибки, на которых спотыкаются чаще всего.

Синтаксис ВЫРАЗИТЬ КАК ЧИСЛО и получение одного числа

Общая форма такая:

ВЫРАЗИТЬ(<Выражение> КАК ЧИСЛО(<Длина>, <Точность>))

Что умеет ВЫРАЗИТЬ для чисел:

  • 🔢 менять разрядность: ВЫРАЗИТЬ(Цена КАК ЧИСЛО(15, 2)) даст два знака после запятой;
  • 🎯 округлять: при уменьшении точности значение округляется по арифметическим правилам, ЧИСЛО(15, 0) — до целого;
  • 🧩 сужать составной тип: если реквизит может хранить и число, и строку, и ссылку, ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) вернёт число, а для нечисловых значений — NULL;
  • 📐 фиксировать тип колонки во временной таблице и в объединении.

Чего ВЫРАЗИТЬ не умеет: преобразовывать строку в число и число в строку. Язык запросов меняет только «размер» внутри одного примитивного типа и сужает составной тип. Для строк с цифрами нужен обходной путь (следующий раздел).

Когда нужен один результат, а не таблица

Если запрос должен вернуть одно число — итог, максимум, последний курс, — есть два варианта.

  1. Агрегат без группировки. ВЫБРАТЬ СУММА(...) без СГРУППИРОВАТЬ ПО всегда возвращает ровно одну строку, даже если подходящих записей нет.
  2. ВЫБРАТЬ ПЕРВЫЕ 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))34
Вниз (для положительных)ВЫРАЗИТЬ(Х - 0.5 КАК ЧИСЛО(15, 0))33
Вверх (для положительных)через ВЫБОР, см. ниже44

Округление вверх нужно, например, чтобы посчитать число коробок по 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.

Как ускорить запросы с числовыми данными

Числа сами по себе не тормозят запрос. Тормозят вычисления в неудачном месте.

  1. Не приводите поле в условии. ГДЕ ВЫРАЗИТЬ(Т.Сумма КАК ЧИСЛО(15, 0)) > 1000 мешает СУБД использовать индекс. Пишите ГДЕ Т.Сумма > 1000 — результат почти тот же, а индекс работает.
  2. Берите готовые итоги. Для регистров накопления используйте виртуальные таблицы Остатки, Обороты, ОстаткиИОбороты вместо суммирования движений вручную. Отборы передавайте в параметры виртуальной таблицы, а не в ГДЕ.
  3. Делите после группировки. СУММА(Сумма) / СУММА(Количество) считается один раз на группу, а деление в каждой строке — миллион раз на миллион строк.
  4. Сужайте составные типы. Для полей вроде Регистратор приведение ВЫРАЗИТЬ(Т.Регистратор КАК Документ.РеализацияТоваровУслуг) убирает лишние соединения со всеми таблицами документов. Это тот же ВЫРАЗИТЬ, только для ссылок.
  5. Промежуточные расчёты — во временные таблицы. Если одно и то же выражение нужно в трёх местах, посчитайте его один раз через ПОМЕСТИТЬ.

Пример: отчёт с выручкой, наценкой и долей

Соберём запрос, где сходятся все приёмы: агрегаты, защита от нуля, округление и 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. Но ВЫРАЗИТЬ в запросе всё равно полезен: он задаёт тип ресурса и убирает длинные дроби в расшифровке.

Ошибки при приведении результата запроса к числу

Диагностика: выберите симптом

Частые вопросы

Какую разрядность указывать в ЧИСЛО(Длина, Точность)?
Ориентируйтесь на реквизиты конфигурации: суммы обычно ЧИСЛО(15, 2), количество — ЧИСЛО(15, 3), курсы — ЧИСЛО(10, 4). Длина включает знаки после запятой.
Можно ли в запросе выразить число как строку?
Нет, ВЫРАЗИТЬ не меняет примитивный тип. Числа в строку переводите после выполнения запроса функцией Формат.
ВЫРАЗИТЬ замедляет запрос?
В списке полей выборки — практически нет. Замедление появляется, когда приведение стоит в условии ГДЕ или в условии соединения по индексируемому полю.
Как проверить, что в поле составного типа лежит число?
Условием ТИПЗНАЧЕНИЯ(Т.Поле) = ТИП(ЧИСЛО). Это удобно, когда нечисловые значения нужно не просто отбросить, а посчитать или вывести отдельно.
Почему в результате запроса число без копеек, хотя в базе они есть?
Проверьте цепочку временных таблиц: где-то по пути стоит ВЫРАЗИТЬ с точностью 0 или с меньшей точностью, чем у реквизита. Приводите колонку к ЧИСЛО(15, 2) один раз, на финальном шаге.