При разработке отчетов или обработок в среде 1С:Предприятие 8 программисты часто сталкиваются с ситуацией, когда стандартная математическая операция (например, умножение) в запросе вызывает ошибку неверные параметры "*". Ситуация выглядит парадоксально: вы проверяете тип значения в консоли запросов, видите там число, но система отказывается выполнять вычисления. Рассмотрим подробно, почему это происходит и как правильно составить запрос, чтобы избежать подобных проблем.
Проанализируем типичный сценарий. В базе данных есть регистр сведений ЗначенияСвойствОбъектов или дополнительные реквизиты. Вы пытаетесь получить значение коэффициента и умножить его на числовую константу или другое поле:
ВЫБРАТЬ
ЗначенияСвойствОбъектов.Значение * 2 КАК ИтоговыйКоэффициент
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
Даже если в конкретной записи хранится значение 0.75, платформа выдаст ошибку. Причина кроется в метаданных. Поле Значение в таких регистрах имеет составной тип данных (например, оно может хранить строку, число, дату или ссылку). Для системы компоновки запросов и SQL-сервера это означает, что тип поля неопределен заранее.
Выясним причину, по которой функция ТИПЗНАЧЕНИЯ может вводить в заблуждение. Когда мы вызываем ТИПЗНАЧЕНИЯ(Коэффициент) в запросе, система анализирует фактическое содержимое конкретной ячейки в уже извлеченной строке. Однако компилятор запроса анализирует структуру таблицы целиком. Поскольку в описании поля значится "составной тип", математические операции запрещены на этапе подготовки запроса, так как система не может гарантировать, что во всех строках окажется число.
Для того чтобы запрос отработал корректно, мы должны явно указать платформе, что значение в данной колонке следует рассматривать как число. Для этого используется оператор ВЫРАЗИТЬ. Рассмотрим, как изменить запрос:
ВЫБРАТЬ
ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ЧИСЛО(15, 3)) * 2 КАК Результат
ИЗ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
При использовании этого оператора важно помнить о следующих правилах:
ЧИСЛО(X, Y) необходимо четко указывать общую длину (X) и количество знаков после запятой (Y). Если вы укажете ЧИСЛО(15, 2), а в базе хранится число с тремя знаками, произойдет округление еще до выполнения умножения.ВЫРАЗИТЬ будет NULL.Часто дополнительные свойства выбираются через левое соединение. Если свойство у объекта не задано, в поле попадет NULL. Любая математическая операция с NULL (например, NULL * 2) всегда возвращает NULL, что может привести к неверным итогам в отчете.
Посмотрим на пример правильной комбинации функций для обеспечения отказоустойчивости запроса:
ВЫБРАТЬ
ЕСТЬNULL(ВЫРАЗИТЬ(ЗначенияСвойствОбъектов.Значение КАК ЧИСЛО(15, 3)), 0) * 2 КАК Итого
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
ПО (ЗначенияСвойствОбъектов.Объект = Номенклатура.Ссылка)
И (ЗначенияСвойствОбъектов.Свойство = &ПараметрСвойство)
Разберем, почему порядок функций важен. В некоторых версиях платформы (особенно 8.2) конструкция ВЫРАЗИТЬ(ЕСТЬNULL(Коэффициент, 0) КАК ЧИСЛО) может вызвать ошибку в конфигураторе, хотя в консоли запросов она будет работать. Наиболее надежным и стандартным способом считается сначала приведение типа через ВЫРАЗИТЬ, а затем замена пустого значения на ноль через ЕСТЬNULL.
Проанализируем ситуацию с точки зрения производительности. Использование ВЫРАЗИТЬ в секции ВЫБРАТЬ практически не замедляет работу. Однако, если вы начнете использовать эту конструкцию в секции ГДЕ или в условиях соединения, это может привести к серьезным проблемам:
Совет: Если вам нужно выполнять сложные расчеты с дополнительными реквизитами в больших таблицах, сначала выгрузите необходимые данные во временную таблицу, сразу выполнив ВЫРАЗИТЬ, а уже в последующих запросах работайте с типизированным числовым полем.
Представим, что нам нужно рассчитать сумму с учетом коэффициента из дополнительных свойств документа. Оптимальный код запроса будет выглядеть так:
ВЫБРАТЬ
Продажи.Документ КАК Документ,
ЕСТЬNULL(ВЫРАЗИТЬ(Свойства.Значение КАК ЧИСЛО(10, 2)), 0) КАК Коэфф
ПОМЕСТИТЬ ВТ_Данные
ИЗ
РегистрНакопления.Продажи КАК Продажи
ЛЕВОЕ СОЕДИНЕНИЕ
РегистрСведений.ЗначенияСвойствОбъектов КАК Свойства
ПО Продажи.Документ = Свойства.Объект
И Свойства.Свойство = &СвойствоКоэффициент;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Данные.Документ,
ВТ_Данные.Коэфф * 100 КАК ПроцентныйКоэффициент
ИЗ
ВТ_Данные КАК ВТ_Данные
Таким образом, мы гарантируем отсутствие ошибок типизации и обеспечиваем корректную обработку пустых значений.