При написании запросов в системе 1С:Предприятие программисты часто сталкиваются с необходимостью проведения арифметических расчетов непосредственно в тексте запроса. Одной из самых распространенных операций является умножение двух полей, например, цены на количество или суммы на ставку налога. Однако, несмотря на кажущуюся простоту, эта операция имеет ряд технических нюансов, несоблюдение которых приводит к ошибкам выполнения или некорректным результатам в отчетах. В этой статье мы подробно разберем, как правильно реализовать умножение в запросе, как избежать типичных ошибок с типами данных и как обрабатывать пустые значения.
Проанализируем ситуацию, когда при попытке умножить два поля система выдает ошибку. Рассмотрим типичный пример кода, который может вызвать затруднения:
ЕСТЬNULL(СчетНаОплатуПоставщика.СуммаДокумента, 0) * ЕСТЬNULL(ОплатаСчетов.СтавкаНДС, 0)
На первый взгляд выражение выглядит логично: мы используем функцию ЕСТЬNULL, чтобы обезопасить себя от пустых значений. Однако ошибка кроется в типах операндов. В языке запросов 1С бинарные арифметические операции (+, –, *, /) применимы исключительно к числовым типам данных (ЧИСЛО).
Выясним причину ошибки в данном примере. Поле СуммаДокумента обычно является числом, а вот поле СтавкаНДС в типовых конфигурациях 1С чаще всего является ссылкой на Перечисление или Справочник. Попытка умножить число на ссылку (объект метаданных) всегда будет приводить к синтаксической ошибке, так как система не знает, какое именно числовое значение скрывается за элементом перечисления НДС20 или НДС10.
Если нам необходимо умножить сумму на ставку НДС, которая хранится как перечисление, мы должны явно преобразовать это значение в число. Разберем, как это сделать с помощью конструкции ВЫБОР. Проанализируем следующий пример:
ВЫБОР
КОГДА ОплатаСчетов.СтавкаНДС = ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС20) ТОГДА 0.20
КОГДА ОплатаСчетов.СтавкаНДС = ЗНАЧЕНИЕ(Перечисление.СтавкиНДС.НДС10) ТОГДА 0.10
ИНАЧЕ 0
КОНЕЦ * ЕСТЬNULL(СчетНаОплатуПоставщика.СуммаДокумента, 0)
В этом случае мы сначала сопоставляем ссылку перечисления с конкретным числовым коэффициентом и только после этого выполняем операцию умножения. Если же в вашей конфигурации ставка НДС хранится в справочнике, и у этого справочника есть числовой реквизит (например, Ставка), то мы можем обратиться к нему через точку:
ОплатаСчетов.СтавкаНДС.Ставка * СчетНаОплатуПоставщика.СуммаДокумента / 100
Важно: Обращение через точку к реквизитам ссылочных полей в запросе допустимо, но следует помнить, что это порождает неявное левое соединение в SQL-запросе, что может сказаться на производительности при работе с очень большими объемами данных.
Рассмотрим ситуацию, когда мы соединяем две таблицы (например, таблицу товаров и таблицу цен). При использовании ЛЕВОЕ СОЕДИНЕНИЕ для некоторых строк данных из правой таблицы может не оказаться. В этом случае на месте цены или количества возникнет значение NULL.
Проанализируем особенность: в языке запросов 1С любая арифметическая операция с NULL (например, 100 * NULL) всегда возвращает результат NULL. Это критично для отчетов, так как итоговая сумма может просто «исчезнуть» из выборки. Посмотрим на пример правильного перемножения полей в табличной части:
ВЫБРАТЬ
(ЕСТЬNULL(ЦеныПоставщиковСрезПоследних.Цена, 0) * ТД.Количество) КАК Сумма
ИЗ
Документ.ПоступлениеТоваровУслуг.Товары КАК ТД
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныПоставщиков.СрезПоследних КАК ЦеныПоставщиковСрезПоследних
ПО ТД.Номенклатура = ЦеныПоставщиковСрезПоследних.Номенклатура
Использование ЕСТЬNULL(Поле, 0) гарантирует, что даже при отсутствии цены в регистре, запрос вернет 0, а не пустое значение, что позволит корректно рассчитать общие итоги.
Рассмотрим более сложный пример, когда нам нужно перемножить данные, полученные из разных источников и объединенные во временной таблице. Посмотрим на структуру такого запроса:
ВЫБРАТЬ
1 КАК Множитель1,
1 КАК ИД
ПОМЕСТИТЬ ВТ1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2, 2;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
3 КАК Множитель2,
1 КАК ИД
ПОМЕСТИТЬ ВТ2
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4, 4;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ1.ИД КАК ВТ1_ИД,
ВТ2.ИД КАК ВТ2_ИД,
ЕСТЬNULL(ВТ1.Множитель1, 0) * ЕСТЬNULL(ВТ2.Множитель2, 0) КАК Поле3
ИЗ
ВТ1 КАК ВТ1
ПОЛНОЕ СОЕДИНЕНИЕ ВТ2 КАК ВТ2
ПО (ВТ1.ИД = ВТ2.ИД)
Здесь мы видим классический пример работы с ПОЛНОЕ СОЕДИНЕНИЕ. Поскольку идентификаторы могут не совпадать в обеих таблицах, обязательное использование ЕСТЬNULL перед умножением является лучшей практикой, обеспечивающей стабильность расчета Поле3.
При выполнении умножения в запросах 1С следует учитывать еще несколько важных технических аспектов:
ВЫБОР КОГДА Знаменатель = 0 ТОГДА 0 ИНАЧЕ Числитель / Знаменатель КОНЕЦ.ВЫРАЗИТЬ. Например: ВЫРАЗИТЬ(Поле1 * Поле2 КАК ЧИСЛО(15, 2)).СУММА(ТЧ.Количество * ТЧ.Цена) КАК ИтоговаяСумма.СГРУППИРОВАТЬ ПО, помните, что в секцию группировки нужно вставлять само выражение (копию формулы), а не псевдоним поля.ВЫБОР КОГДА Флаг ТОГДА Сумма ИНАЧЕ 0 КОНЕЦ.Подведем итог. Умножение в запросе — мощный инструмент, который позволяет перенести часть бизнес-логики на уровень СУБД. Главное — всегда контролировать типы данных операндов, преобразовывать перечисления в числа и защищать расчеты от NULL с помощью функции ЕСТЬNULL. Соблюдение этих правил позволит вам создавать надежные и быстрые отчеты.