При написании запросов в платформе «1С:Предприятие» разработчики часто сталкиваются со сложной логикой ветвления данных. Для реализации условий непосредственно в теле запроса используется стандартная конструкция ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ. Однако при работе с перечислениями, справочниками или предопределенными элементами новички часто совершают типичную синтаксическую ошибку, которая приводит к отказу выполнения запроса СУБД.
В этой статье мы подробно разберем, почему возникает ошибка при обращении к функции ЗНАЧЕНИЕ() внутри условного оператора запроса, как правильно составить логическое выражение, а также рассмотрим важные вопросы оптимизации запросов при работе с составными типами данных и перечислениями.
Рассмотрим исходный фрагмент кода запроса, который вызвал затруднения у автора темы на форуме:
ВЫБОР
КОГДА
Значение(Перечисление.ВидыОперацийРасходнаяНакладная.ВозвратСОтветХранения)
ТОГДА
0
ИНАЧЕ
ВТ_Маршрут.Накладная.СуммаДокумента - ЕСТЬNULL(ВложенныйЗапрос.Скидка, 0)
КОНЕЦ
При попытке сохранить или выполнить такой запрос платформа выдаст синтаксическую ошибку. Давайте выясним ее причину.
Конструкция ВЫБОР в языке запросов 1С является прямым аналогом оператора CASE WHEN в SQL. Главное правило ее использования заключается в следующем: после ключевого слова КОГДА всегда должно следовать логическое выражение (условие), результатом вычисления которого является тип Булево (то есть значение ИСТИНА или ЛОЖЬ).
В ошибочном примере кода сразу после слова КОГДА указана конструкция:
Значение(Перечисление.ВидыОперацийРасходнаяНакладная.ВозвратСОтветХранения)
Функция ЗНАЧЕНИЕ() возвращает прямую ссылку на конкретный элемент метаданных (в данном случае тип значения — ПеречислениеСсылка.ВидыОперацийРасходнаяНакладная). Сама по себе ссылка не является логическим условием. Платформа 1С не может неявно привести ссылку на перечисление к типу Булево, из-за чего компилятор запросов сообщает об ошибке.
Чтобы исправить ошибку, нам необходимо превратить ссылку на значение перечисления в полноценное логическое выражение сравнения. Мы должны сопоставить конкретное поле из нашей таблицы базы данных с искомым значением перечисления с помощью оператора равенства «=».
Проанализируем правильный вариант написания конструкции ВЫБОР:
ВЫБОР
КОГДА ВТ_Маршрут.Накладная.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРасходнаяНакладная.ВозвратСОтветХранения)
ТОГДА 0
ИНАЧЕ ВТ_Маршрут.Накладная.СуммаДокумента - ЕСТЬNULL(ВложенныйЗапрос.Скидка, 0)
КОНЕЦ
В данном случае мы берем поле ВидОперации из связанной таблицы документа Накладная и сравниваем его со статическим значением перечисления. Результатом этого сравнения будет либо ИСТИНА, либо ЛОЖЬ, что полностью соответствует правилам синтаксиса оператора ВЫБОР.
Функция ЗНАЧЕНИЕ() (или её англоязычный аналог VALUE()) — это специализированная системная конструкция языка запросов 1С. Она предназначена для быстрого и прямого обращения к предопределенным элементам конфигурации без необходимости передавать их через параметры запроса.
Давайте выделим ключевые правила и ограничения при работе с этой функцией:
ЗНАЧЕНИЕ() можно указывать только жестко прописанные в конфигураторе имена объектов метаданных. Передавать переменные, вычисляемые поля или собирать строки динамически внутри этой функции категорически запрещено.ЗНАЧЕНИЕ(Перечисление.ИмяПеречисления.ИмяЗначения)ЗНАЧЕНИЕ(Справочник.Валюты.Рубль)ЗНАЧЕНИЕ(ПланСчетов.Хозрасчетный.Товары)ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход) или ЗНАЧЕНИЕ(ВидДвиженияБухгалтерии.Дебет).ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).ЗНАЧЕНИЕ(&ИмяПараметра) нельзя. Если значение должно определяться динамически в коде на встроенном языке, вам следует отказаться от функции ЗНАЧЕНИЕ() в пользу стандартных параметров запроса.Посмотрим на пример, когда значение перечисления передается в запрос динамически через параметр:
// В тексте запроса:
ВЫБОР
КОГДА ВТ_Маршрут.Накладная.ВидОперации = &ПараметрВидОперации
ТОГДА 0
ИНАЧЕ ВТ_Маршрут.Накладная.СуммаДокумента - ЕСТЬNULL(ВложенныйЗапрос.Скидка, 0)
КОНЕЦ
Затем во встроенном языке 1С мы передаем конкретную ссылку в этот параметр:
Запрос = Новый Запрос;
Запрос.Текст = "..."; // Текст запроса с параметром
Запрос.УстановитьПараметр("ПараметрВидОперации", Перечисления.ВидыОперацийРасходнаяНакладная.ВозвратСОтветХранения);
Результат = Запрос.Выполнить();
Рассмотрим ситуацию, когда поле, к которому мы обращаемся, имеет составной тип данных. Например, поле Накладная в нашей временной таблице ВТ_Маршрут может хранить ссылки на различные документы: Документ.РасходнаяНакладная, Документ.ПеремещениеТоваров или Документ.ОтчетОРозничныхПродажах.
Когда мы пишем условие ВТ_Маршрут.Накладная.ВидОперации, СУБД вынуждена строить неявные левые соединения (LEFT JOIN) со всеми таблицами документов, входящих в этот составной тип, чтобы попытаться найти у них реквизит ВидОперации. Это крайне негативно сказывается на скорости выполнения запроса и может приводить к избыточному сканированию индексов и таблиц.
Чтобы избежать падения производительности, рекомендуется предварительно ограничивать тип проверяемого поля с помощью оператора ССЫЛКА. Проанализируем оптимизированный вариант условия:
ВЫБОР
КОГДА ВТ_Маршрут.Накладная ССЫЛКА Документ.РасходнаяНакладная
И ВТ_Маршрут.Накладная.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРасходнаяНакладная.ВозвратСОтветХранения)
ТОГДА 0
ИНАЧЕ ВТ_Маршрут.Накладная.СуммаДокумента - ЕСТЬNULL(ВложенныйЗапрос.Скидка, 0)
КОНЕЦ
Благодаря явному указанию типа через ССЫЛКА Документ.РасходнаяНакладная транслятор запросов 1С точно определит, к какой физической таблице базы данных нужно обратиться для проверки реквизита ВидОперации. Лишние неявные соединения с другими таблицами документов создаваться не будут.
Мало кто из начинающих разработчиков знает, что в языке запросов 1С поддерживается альтернативный синтаксис конструкции ВЫБОР. Он очень похож на классический оператор switch или case в традиционных языках программирования (например, C#, Java или JavaScript). Он позволяет указать проверяемое поле сразу после ключевого слова ВЫБОР, избавляя от необходимости дублировать имя этого поля в каждом условии КОГДА.
Давайте посмотрим на пример такой реализации:
ВЫБОР ВТ_Маршрут.Накладная.ВидОперации
КОГДА ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРасходнаяНакладная.ВозвратСОтветХранения) ТОГДА 0
КОГДА ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРасходнаяНакладная.РеализацияКлиенту) ТОГДА 1
ИНАЧЕ 2
КОНЕЦ
Преимущества данного подхода:
Ограничения:
КОГДА нельзя использовать сложные логические операторы (такие как И, ИЛИ, НЕ, ССЫЛКА или сравнения больше/меньше).В практической работе иногда возникает задача вывести текстовое имя перечисления (его идентификатор или синоним) для последующей выгрузки во внешние системы (например, через форматы XML, JSON или XLS).
Важно помнить, что напрямую получить строковое имя значения перечисления (например, получить строку "ВозвратСОтветХранения") стандартными средствами запроса невозможно. База данных хранит лишь внутренние идентификаторы (ссылки).
Однако мы можем решить эту задачу двумя способами:
Способ 1. Использование функции ПРЕДСТАВЛЕНИЕ(). Данная функция возвращает пользовательский синоним значения перечисления, настроенный в конфигураторе (например, "Возврат с ответственного хранения"):
ВЫБРАТЬ
ПРЕДСТАВЛЕНИЕ(ВТ_Маршрут.Накладная.ВидОперации) КАК ТекстовоеПредставлениеОперации
ИЗ
ВТ_Маршрут КАК ВТ_Маршрут
Способ 2. Жесткое сопоставление через ВЫБОР КОГДА. Если вам необходим именно системный код (идентификатор) значения для интеграции, то придется прописать его вручную в запросе:
ВЫБРАТЬ
ВЫБОР
КОГДА ВТ_Маршрут.Накладная.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРасходнаяНакладная.ВозвратСОтветХранения)
ТОГДА "ВозвратСОтветХранения"
КОГДА ВТ_Маршрут.Накладная.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРасходнаяНакладная.Продажа)
ТОГДА "Продажа"
ИНАЧЕ "Прочее"
КОНЕЦ КАК СистемныйКодОперации
ИЗ
ВТ_Маршрут КАК ВТ_Маршрут
Подведем итоги и зафиксируем правила, которые помогут вам избежать ошибок при написании условных выражений в запросах 1С:
КОГДА всегда должно находиться логическое выражение, возвращающее Булево. Использование одиночных ссылок или вызовов функции ЗНАЧЕНИЕ() без операторов сравнения недопустимо.ЗНАЧЕНИЕ(), так как она выполняется быстрее и не требует передачи параметров из кода.ССЫЛКА для оптимизации производительности СУБД.ВЫБОР Поле КОГДА Значение ТОГДА ... КОНЕЦ.