Приведение значения к числу оператором ВЫРАЗИТЬ в запросе
Содержание
Чтобы привести значение к числу в запросе 1С, используют оператор ВЫРАЗИТЬ(Выражение КАК ЧИСЛО(Длина, Точность)). Он решает две задачи: достаёт число из поля составного типа и ограничивает разрядность результата. Синтаксис один и тот же в толстом и тонком клиенте платформы 8.3.
Важная оговорка сразу, чтобы не терять время: ВЫРАЗИТЬ не парсит текст. Строку "123" в число 123 он не превратит — для поля-строки результат будет NULL. Ниже разберём, где оператор действительно работает, что он делает с NULL и пустыми значениями, как приводить число по условию через ВЫБОР и чем приведение грозит скорости запроса.
Синтаксис оператора ВЫРАЗИТЬ и параметры числа
Общая форма записи:
ВЫРАЗИТЬ(<Выражение> КАК <Тип>)
Для числового типа указывают длину и точность:
ВЫРАЗИТЬ(ПродажиОбороты.СуммаОборот КАК ЧИСЛО(15, 2))
Здесь 15 — общее количество знаков, 2 — количество знаков после запятой. То есть до запятой остаётся 13 разрядов, после — 2. Точность не может быть больше длины: запись ЧИСЛО(5, 8) некорректна.
Точность можно не указывать — ЧИСЛО(10) означает целое число на 10 знаков. Длину тоже допустимо опустить (ЧИСЛО), но тогда разрядность платформа подберёт сама, и смысла в таком приведении обычно нет.
Полный пример запроса, который приводит сумму к фиксированной разрядности:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
ВЫРАЗИТЬ(СУММА(Продажи.Сумма) КАК ЧИСЛО(15, 2)) КАК Сумма
ИЗ
РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
Приведение к ЧИСЛО не округляет по математическим правилам — лишние знаки после запятой отбрасываются. Если нужна сумма с копейками, ставьте точность 2 и следите, чтобы исходные данные уже были округлены на этапе проводок.
Зачем приводить поле составного типа к числу
Главный честный сценарий для ВЫРАЗИТЬ — поле составного типа. Это поле, которое может хранить значения нескольких типов: реквизит с типом «Число, Строка, Дата», поле Значение из регистра сведений «Дополнительные сведения
», характеристики, значения свойств.
Платформа не даёт сразу считать такое поле числом — сначала его надо «сузить» до конкретного типа:
ВЫБРАТЬ
ВЫРАЗИТЬ(ДополнительныеСведения.Значение КАК ЧИСЛО(10, 2)) КАК ЧисловоеЗначение
ИЗ
РегистрСведений.ДополнительныеСведения КАК ДополнительныеСведения
Логика простая:
- 🔹 если в строке регистра лежит число — вернётся это число;
- 🔹 если лежит строка, дата или ссылка — вернётся
NULL; - 🔹 арифметику и сравнение с другим числом можно делать только после приведения.
Без приведения обратиться к такому полю как к числу не выйдет — платформа не знает заранее, какой тип в нём лежит, и сравнение или сложение вызовет ошибку несовместимости типов. ВЫРАЗИТЬ снимает эту неопределённость: он говорит системе «считай это число, а если там не число — верни NULL».
Отдельный случай — ссылочные составные поля вроде Регистратор у регистра накопления. К числу их не приводят, но тот же оператор достаёт реквизиты конкретного документа:
ВЫРАЗИТЬ(Продажи.Регистратор КАК Документ.РеализацияТоваровУслуг).Номер
Про такие ссылочные приведения важно помнить при оптимизации — вернёмся к этому ниже.
Что возвращает ВЫРАЗИТЬ для NULL и пустых значений
NULL оператор не трогает. ВЫРАЗИТЬ от NULL всегда даёт NULL, а не ноль. Это частый источник ошибок в итоговых суммах и в условиях ГДЕ.
Разделяйте три разных «пустых» состояния:
| Состояние | Что это | Чем обрабатывать |
|---|---|---|
NULL | нет записи (левое соединение не нашло пару, остаток отсутствует) | ЕСТЬNULL(Поле, 0) |
0 | число ноль, запись есть | обычное сравнение |
| Пустая ссылка | реквизит заполнен «пустым» значением типа | сравнение с ЗНАЧЕНИЕ(...ПустаяСсылка) |
Чтобы вместо NULL получить ноль, оборачивайте результат в ЕСТЬNULL:
ВЫБРАТЬ
ЕСТЬNULL(ВЫРАЗИТЬ(ДопСведения.Значение КАК ЧИСЛО(10, 2)), 0) КАК Значение
ИЗ
РегистрСведений.ДополнительныеСведения КАК ДопСведения
Если забыть ЕСТЬNULL и просто сложить поле с NULL, вся сумма станет NULL: в арифметике любое действие с NULL даёт NULL. В отчёте вы увидите пустую ячейку вместо итога.
Порядок обёрток имеет значение. Сначала ВЫРАЗИТЬ сужает тип, затем ЕСТЬNULL подставляет ноль. Если сделать наоборот — ЕСТЬNULL(Поле, 0) на составном поле, — платформа сравнит типы числа и составного значения и может выдать ошибку.
Приведение числа по условию через оператор ВЫБОР
Когда число нужно собрать по условию, работает связка ВЫБОР … КОГДА … ТОГДА … ИНАЧЕ … КОНЕЦ. Это аналог CASE в SQL:
ВЫБРАТЬ
ВЫБОР
КОГДА Заказы.Проведен
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ КАК Флаг
ИЗ
Документ.ЗаказПокупателя КАК Заказы
Здесь булево значение превращается в число 1 или 0 — удобно для подсчётов и дальнейшего суммирования.
ВЫБОР и ВЫРАЗИТЬ часто идут вместе: сначала условием отбираем нужные строки, потом приводим значение к числу.
ВЫБРАТЬ
ВЫБОР
КОГДА ДопСведения.Свойство = &СвойствоВес
ТОГДА ВЫРАЗИТЬ(ДопСведения.Значение КАК ЧИСЛО(10, 3))
ИНАЧЕ 0
КОНЕЦ КАК Вес
ИЗ
РегистрСведений.ДополнительныеСведения КАК ДопСведения
Несколько правил, чтобы ВЫБОР не ломался:
- Все ветки
ТОГДАиИНАЧЕдолжны возвращать совместимые типы. Если одна ветка даёт число, а другая — строку, итоговый тип поля станет составным. - Ветку
ИНАЧЕлучше указывать явно. Без неё непопавшие строки получатNULL, а не ноль. - Условия проверяются сверху вниз — сработает первое истинное
КОГДА.
Через ВЫБОР удобно задавать числовую шкалу — например, присвоить группу по сумме сделки:
ВЫБОР
КОГДА Заказы.СуммаДокумента >= 100000 ТОГДА 3
КОГДА Заказы.СуммаДокумента >= 10000 ТОГДА 2
ИНАЧЕ 1
КОНЕЦ КАК Категория
Порядок условий здесь критичен: более крупный порог идёт первым, иначе сумма 150000 попадёт в ветку >= 10000 и получит категорию 2 вместо 3.
Можно ли превратить строку в число в запросе
Короткий ответ — средствами языка запросов нет. ВЫРАЗИТЬ приводит только значения, которые уже являются числом внутри составного типа. Текстовое представление "1 250,00" он разобрать не умеет.
Если поле хранит число именно как строку, есть три пути:
- 🔹 Исправить источник. Чаще всего строковое хранение числа — ошибка проектирования. Поменяйте тип реквизита на
Число— это правильнее любых обходов в запросе. - 🔹 Преобразовать в коде, а не в запросе. Получите значение в переменную и используйте функцию
Число()встроенного языка. Пробелы-разделители и запятую стоит убрать заранее:
Текст = СтрЗаменить(СокрЛП(СтрокаСуммы), " ", "");
Текст = СтрЗаменить(Текст, ",", ".");
Сумма = Число(Текст);
Такой разбор в тексте запроса недоступен — только в модуле.
- 🔹 Готовить данные во временной таблице. Если строки надо сопоставить, иногда проще обработать их процедурно и положить в таблицу значений, которую вернуть в запрос как параметр.
Обратная задача — показать число строкой — в запросе тоже не делается функцией форматирования. Для вывода применяйте Формат(Число, "ЧГ=0; ЧДЦ=2") уже в коде или в макете, а сравнение разрядностей между подзапросами решайте через ВЫРАЗИТЬ к одному типу ЧИСЛО(длина, точность).
Как приведение к числу влияет на скорость запроса
Приведение к примитивному типу (ЧИСЛО, СТРОКА, ДАТА) выполняется на стороне СУБД и стоит дёшево. На миллионах строк разница почти незаметна. А вот несколько моментов реально влияют на план запроса.
Приведение ссылочного поля сокращает соединения. Когда вы обращаетесь к реквизиту составного ссылочного поля без ВЫРАЗИТЬ, платформа добавляет соединение со всеми таблицами, которые теоретически могут там лежать. Явное ВЫРАЗИТЬ(Регистратор КАК Документ.РеализацияТоваровУслуг) оставляет соединение с одной таблицей. Для числовых полей это не про тип числа, но принцип тот же: приведение помогает платформе, а не мешает.
Приведение в условии мешает индексу. Если обернуть индексированное поле в ВЫРАЗИТЬ прямо в секции ГДЕ или в условии соединения, СУБД не сможет использовать индекс по этому полю — придётся сканировать таблицу.
Приводите значение в списке выборки (ВЫБРАТЬ), а отбор стройте по «сырому» полю. Тогда индекс остаётся рабочим, а разрядность вы фиксируете уже на результате.
Разрядность в объединениях и агрегатах. Функция СУММА расширяет разрядность результата, а в ОБЪЕДИНИТЬ типы колонок должны совпадать. Единое ВЫРАЗИТЬ(… КАК ЧИСЛО(15, 2)) в каждой части объединения приводит колонки к одной разрядности и снимает ошибки несовместимости типов.
Единая разрядность при объединении и соединении таблиц
Отдельная боль — когда число собирается из нескольких источников. Платформа строго следит за типами колонок, и расхождение в разрядности часто всплывает именно здесь.
В ОБЪЕДИНИТЬ ВСЕ каждая колонка в разных частях запроса должна иметь один тип. Если в первой части сумма пришла как ЧИСЛО(15, 2), а во второй посчиталась через КОЛИЧЕСТВО или другую агрегатную функцию с иной разрядностью, тип колонки на выходе станет составным или запрос откажется выполняться. Приведите обе части к одной мерке:
ВЫБРАТЬ
ВЫРАЗИТЬ(СУММА(Приход.Сумма) КАК ЧИСЛО(15, 2)) КАК Сумма
ИЗ
РегистрНакопления.Поступления КАК Приход
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
ВЫРАЗИТЬ(-СУММА(Расход.Сумма) КАК ЧИСЛО(15, 2))
ИЗ
РегистрНакопления.Списания КАК Расход
Тот же приём спасает от ошибки «Переполнение разрядности числового поля». Она возникает, когда промежуточный результат (особенно после СУММА по большому регистру или умножения количества на цену) не влезает в автоматически выбранную разрядность. Явное приведение к ЧИСЛО с запасом по длине задаёт предел и убирает переполнение:
ВЫРАЗИТЬ(СУММА(Строки.Количество * Строки.Цена) КАК ЧИСЛО(18, 2))
При соединении таблиц по числовому ключу (например, по коду или номеру) приводите оба поля к одной разрядности. Если ключи заданы как ЧИСЛО(8, 0) и ЧИСЛО(12, 0), соединение может не находить совпадения из-за разницы типов — ВЫРАЗИТЬ в условии соединения здесь оправдан, несмотря на индекс.
Исключение про индекс из прошлого раздела остаётся в силе: приводите в условии соединения только когда без этого типы реально не сходятся. Если ключи одной разрядности — ничего оборачивать не нужно.
Типичные ошибки при приведении к числу
Ошибки с ВЫРАЗИТЬ почти всегда сводятся к четырём ситуациям.
Ждут разбор строки. Самое частое: поле хранит "100" строкой, а от ВЫРАЗИТЬ ждут число 100. Вернётся NULL. Лечится не запросом, а типом реквизита или обработкой в коде.
Теряют итог из-за NULL. Сумма без ЕСТЬNULL по левому соединению превращается в пустую ячейку. Правило: любое поле из левого соединения, которое идёт в арифметику, оборачивайте в ЕСТЬNULL(…, 0).
Срезают разрядность и удивляются «пропавшим копейкам». ЧИСЛО(10, 0) отбросит всё после запятой без округления. Если считаете деньги — точность 2, и помните, что это усечение, а не округление.
Приводят в условии и роняют скорость. ВЫРАЗИТЬ на индексированном поле в ГДЕ заставляет СУБД сканировать таблицу. Переносите приведение в список выборки.
Не путайте оператор запроса ВЫРАЗИТЬ с функцией встроенного языка Число(). Первый работает внутри текста запроса и типы не парсит; вторая — в коде модуля и как раз разбирает строковое представление.
Чем ВЫРАЗИТЬ отличается от ЕСТЬNULL?
NULL. Это разные задачи, их часто применяют вместе.Что вернёт ВЫРАЗИТЬ, если значение не подходит под тип?
NULL. Если в составном поле лежало не число, а строка или дата, приведение к ЧИСЛО даст NULL, а не ноль и не ошибку.Нужно ли приводить обычный числовой реквизит?
Число, ВЫРАЗИТЬ не нужен. Оператор полезен только для составных полей или когда надо зафиксировать разрядность результата.Как ограничить знаки после запятой в итоговой сумме?
ВЫРАЗИТЬ(… КАК ЧИСЛО(15, 2)). Учтите, что лишние знаки отбрасываются, а не округляются математически.Можно ли в ВЫРАЗИТЬ указать точность больше длины?
ЧИСЛО(5, 8) вызовет ошибку; корректно, например, ЧИСЛО(8, 5).