Почему не работает условие ВЫБОР в запросе при сравнении со строкой и как правильно обращаться к перечислениям?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С:Предприятие часто возникают ситуации, когда логика, работающая в обычном коде модуля, отказывается работать внутри языка запросов. Одна из самых распространенных проблем — попытка сравнить значение перечисления (например, статус документа) с его строковым представлением. Разберем подробно ситуацию, когда конструкция ВЫБОР (CASE) игнорирует условия, и выясним, как правильно работать с типами данных в запросах.

Суть проблемы: Сравнение несравнимого

Рассмотрим пример запроса, который вызывает вопросы у многих начинающих разработчиков. Допустим, нам нужно в зависимости от статуса заказа вывести определенную сумму или ноль. Ошибочный код выглядит следующим образом:


ВЫБОР 
    КОГДА ЗаказПокупателяТовары.Ссылка.Статус = "Согласование" 
       ИЛИ ЕСТЬNULL(ЗаказПокупателяТовары.Ссылка.Статус, "") = "ВыписанСчет" 
    ТОГДА ЗаказПокупателяТовары.Сумма 
КОНЕЦ

На первый взгляд, логика кажется верной: мы проверяем, равен ли статус строке "Согласование" или "ВыписанСчет". Однако этот код работать не будет. Платформа либо выдаст ошибку "Несовместимые типы", либо (в некоторых старых версиях или контекстах) условие просто всегда будет вычисляться как ЛОЖЬ. **Почему это происходит?** Язык запросов 1С — это строго типизированный язык (аналог SQL). В отличие от встроенного языка 1С, где иногда возможно неявное приведение типов, в запросах: 1. Поле Статус имеет тип **ПеречислениеСсылка** (ссылка на объект базы данных). 2. Значение "Согласование" — это **Строка** (литерал). Для SQL-сервера и движка запросов 1С ссылка на объект и строка с его названием — это совершенно разные сущности, которые никогда не могут быть равны друг другу.

Решение 1: Использование оператора ЗНАЧЕНИЕ()

Самый простой и быстрый способ исправить запрос, если вы точно знаете имя перечисления и его значение в конфигураторе — использовать специальный оператор языка запросов ЗНАЧЕНИЕ(). Этот оператор позволяет обращаться к предопределенным данным (перечислениям, предопределенным элементам справочников, планам счетов) напрямую в тексте запроса. Правильный синтаксис выглядит так: ЗНАЧЕНИЕ(Перечисление.<ИмяПеречисления>.<ИмяЗначения>) Давайте исправим наш исходный пример. Предположим, что перечисление в конфигураторе называется СтатусыЗаказовПокупателей. Тогда корректный запрос будет выглядеть так:


ВЫБОР 
    КОГДА ЗаказПокупателяТовары.Ссылка.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовПокупателей.Согласование) 
       ИЛИ ЗаказПокупателяТовары.Ссылка.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовПокупателей.ВыписанСчет) 
    ТОГДА ЗаказПокупателяТовары.Сумма 
КОНЕЦ

**Важные нюансы:** * Имя перечисления и имя значения должны **в точности** совпадать с тем, как они заданы в дереве метаданных (Конфигураторе), а не с тем, как они отображаются пользователю (Синоним). * Оператор ЗНАЧЕНИЕ() обрабатывается на этапе компиляции запроса, превращаясь в уникальный идентификатор (GUID) соответствующей ссылки.

Решение 2: Использование параметров запроса

Второй подход, который часто является более профессиональным и гибким — использование параметров. Это особенно полезно, если статус, с которым мы сравниваем, может меняться динамически (например, выбирается пользователем на форме отчета). В тексте запроса мы используем амперсанд &:


ВЫБОР 
    КОГДА ЗаказПокупателяТовары.Ссылка.Статус = &НужныйСтатус
    ТОГДА ЗаказПокупателяТовары.Сумма 
КОНЕЦ

А в коде модуля, перед выполнением запроса, мы устанавливаем значение параметра:


Запрос.УстановитьПараметр("НужныйСтатус", Перечисления.СтатусыЗаказовПокупателей.Согласование);

**Преимущества параметров:** 1. **Кэширование плана запроса:** СУБД строит план выполнения один раз. Если вы используете параметры, план переиспользуется. Если вы меняете текст запроса (подставляя разные значения через ЗНАЧЕНИЕ или конкатенацию строк), СУБД каждый раз тратит время на построение нового плана. 2. **Читаемость:** Код становится чище. Однако, если логика жестко завязана на конкретные бизнес-процессы (например, "Заказы в статусе 'Закрыт' редактировать нельзя"), использование ЗНАЧЕНИЕ() ("хардкод" в запросе) вполне допустимо и является стандартом.

Работа с NULL в условиях ВЫБОР

В исходном вопросе автор использовал конструкцию: ЕСТЬNULL(ЗаказПокупателяТовары.Ссылка.Статус, """") Давайте проанализируем, нужно ли это. 1. Если поле Статус получено из реальной таблицы (как в примере), оно редко бывает NULL, если только это не результат левого соединения. 2. Если Статус равен NULL, то любое прямое сравнение (например, Статус = ЗНАЧЕНИЕ(...)) вернет результат **NULL** (что эквивалентно ЛОЖЬ в логических условиях ВЫБОР). Таким образом, если вы не используете блок ИНАЧЕ для специфической обработки отсутствующих данных, предварительное преобразование через ЕСТЬNULL часто избыточно. Условие просто не выполнится, и сработает ветка выхода. Однако, если вы хотите явно обработать ситуацию, когда ссылка пустая (не заполнена пользователем), следует использовать:


... ИЛИ ЗаказПокупателяТовары.Ссылка.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовПокупателей.ПустаяСсылка) ...

Особенности конструкции ВЫБОР (CASE)

В ходе обсуждения был затронут интересный вопрос: **что будет, если не написать ИНАЧЕ?** Синтаксис оператора выглядит так:


ВЫБОР
    КОГДА <Условие1> ТОГДА <Результат1>
    ...
    [ИНАЧЕ <РезультатИначе>]
КОНЕЦ

Если ни одно из условий КОГДА не выполнилось, и блок ИНАЧЕ отсутствует, оператор вернет значение **NULL**. Это критически важно учитывать, если результат выборки будет участвовать в математических операциях. Пример опасной ситуации:


// Если статус не совпал, вернется NULL.
// Любое число + NULL = NULL.
Сумма + ВЫБОР КОГДА ... КОНЕЦ 

Поэтому хорошей практикой считается всегда добавлять ИНАЧЕ, возвращая значение по умолчанию (например, 0 или пустую строку):


ВЫБОР 
    КОГДА ЗаказПокупателяТовары.Ссылка.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказовПокупателей.Согласование)
    ТОГДА ЗаказПокупателяТовары.Сумма 
    ИНАЧЕ 0
КОНЕЦ

Резюме

Чтобы ваши запросы работали корректно и предсказуемо, следуйте этим правилам:

  1. Никогда не сравнивайте ссылочные поля (Справочники, Перечисления, Документы) со строками.
  2. Используйте оператор ЗНАЧЕНИЕ(Тип.Имя.Элемент) для обращения к предопределенным данным непосредственно в запросе.
  3. Используйте параметры (&Параметр), если значения передаются извне или могут меняться.
  4. Помните, что отсутствие блока ИНАЧЕ в конструкции ВЫБОР приводит к возврату NULL в случае невыполнения условий.
← На главную