Конструкция ВЫБОР в языке запросов 1С: ветвления, вложенность, ошибки и скорость

· 11 мин

Содержание

Условие в запросе 1С пишется конструкцией ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Это аналог CASE WHEN из SQL: платформа проверяет ветки КОГДА сверху вниз и возвращает значение первой истинной. Если ни одна не сработала и ИНАЧЕ нет, результат равен NULL.

Оператор ВЫБОР работает в любом месте запроса, где допустимо выражение: в списке полей, в ГДЕ, в условии соединения, в СГРУППИРОВАТЬ ПО, УПОРЯДОЧИТЬ ПО и внутри агрегатных функций. Отдельной функции КОГДА в языке запросов нет: КОГДА — это ключевое слово ветки внутри ВЫБОР. Синтаксис одинаков для всех конфигураций на платформе 8.3 — БП, ЗУП, УТ, ERP.

Синтаксис оператора ВЫБОР по частям

Полная форма выглядит так:

ВЫБОР
    КОГДА Условие1 ТОГДА Значение1
    КОГДА Условие2 ТОГДА Значение2
    ИНАЧЕ ЗначениеПоУмолчанию
КОНЕЦ КАК ИмяПоля

Из чего она состоит:

  • 🔹 ВЫБОР — открывает конструкцию. Английский вариант — CASE.
  • 🔹 КОГДА … ТОГДА … — ветка. Веток может быть сколько угодно, минимум одна. Английский вариант — WHEN … THEN ….
  • 🔹 ИНАЧЕ — значение, если ни одно условие не выполнено. Необязательна. Английский вариант — ELSE.
  • 🔹 КОНЕЦ — закрывает конструкцию. Без него запрос не разберётся. Английский вариант — END.
  • 🔹 КАК ИмяПоля — псевдоним. Для поля выборки без него платформа назовёт колонку сама, и обращаться к ней из кода будет неудобно.

Что важно знать о форме записи

В языке запросов 1С есть только «поисковая» форма: после каждого КОГДА стоит полноценное логическое условие. Сокращённой записи вида ВЫБОР Поле КОГДА 1 ТОГДА …, как в некоторых диалектах SQL, нет. Каждый раз пишите поле заново: КОГДА Поле = 1 ТОГДА ….

Регистр ключевых слов значения не имеет. Выбор Когда … Конец и ВЫБОР КОГДА … КОНЕЦ равнозначны. Заглавные буквы — просто привычка, которая помогает читать длинный текст запроса.

Ветки проверяются по порядку, и срабатывает только первая истинная. Если условия пересекаются, ставьте самое узкое выше. Иначе широкое условие «съест» все строки.

Простые условия в ВЫБОР: сравнение значений

Самый частый сценарий — превратить значение поля в понятную подпись или число.

Сравнение с числом и строкой

ВЫБРАТЬ
    Товары.Номенклатура,
    Товары.Количество,
    ВЫБОР
        КОГДА Товары.Количество = 0 ТОГДА "Нет в наличии"
        КОГДА Товары.Количество < 10 ТОГДА "Мало"
        ИНАЧЕ "Достаточно"
    КОНЕЦ КАК Остаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Товары

Строка с количеством 5 попадёт во вторую ветку. Строка с количеством 0 — в первую, хотя условие «меньше 10» для неё тоже истинно: первая ветка стоит выше.

Сравнение со ссылками и перечислениями

Для предопределённых значений используйте функцию ЗНАЧЕНИЕ():

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

Имя перечисления выше — пример. Точное имя смотрите в дереве конфигурации: в разных конфигурациях перечисления называются по-разному.

Для пустой ссылки — та же функция: ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).

Проверка типа: ССЫЛКА

Если поле составного типа — например, регистратор регистра — ветку можно строить по типу значения:

ВЫБОР
    КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг ТОГДА "Приход"
    КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг ТОГДА "Расход"
    ИНАЧЕ "Прочее"
КОНЕЦ КАК ТипДвижения

Сравнение с NULL

Здесь ошибаются чаще всего. Условие КОГДА Поле = NULL никогда не будет истинным: сравнение с NULL через знак равенства не работает . Правильная запись — ЕСТЬ NULL:

ВЫБОР
    КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL ТОГДА 0
    ИНАЧЕ Остатки.КоличествоОстаток
КОНЕЦ

NULL появляется после левого соединения, когда справа не нашлось пары. Его же получите, если у ВЫБОР нет ИНАЧЕ и ни одна ветка не сработала.

Составные логические выражения внутри КОГДА

После КОГДА можно писать всё, что допустимо в ГДЕ. Это и операторы И, ИЛИ, НЕ, и проверки В, МЕЖДУ, ПОДОБНО.

ОператорПример условияКогда удобен
ИКОГДА Сумма > 0 И Оплачено = ЛОЖЬНужно одновременно несколько признаков
ИЛИКОГДА Склад = &Склад1 ИЛИ Склад = &Склад2Альтернативные значения
НЕКОГДА НЕ ПроведенОтрицание булева поля
ВКОГДА Статус В (&СписокСтатусов)Список значений из параметра
МЕЖДУКОГДА Дата МЕЖДУ &Начало И &КонецДиапазон дат или чисел
ПОДОБНОКОГДА Наименование ПОДОБНО "%услуг%"Поиск по части строки
В ИЕРАРХИИКОГДА Номенклатура В ИЕРАРХИИ (&Группа)Принадлежность группе справочника

Скобки и приоритет

И выполняется раньше, чем ИЛИ. Условие А ИЛИ Б И В читается как А ИЛИ (Б И В). Если вы имели в виду другое — ставьте скобки явно. Лишние скобки ничего не стоят, а потерянные дают неправильный результат без всякой ошибки.

Пример: сегментация покупателей

ВЫБОР
    КОГДА Продажи.Сумма >= 1000000
        И Продажи.КоличествоДокументов >= 12 ТОГДА "Ключевой"
    КОГДА Продажи.Сумма >= 1000000
        ИЛИ Продажи.КоличествоДокументов >= 12 ТОГДА "Крупный"
    КОГДА Продажи.Сумма > 0 ТОГДА "Обычный"
    ИНАЧЕ "Без продаж"
КОНЕЦ КАК Сегмент

Покупатель с оборотом 1 200 000 и 15 документами попадёт в «Ключевые». С оборотом 1 200 000 и 3 документами — в «Крупные». С оборотом 50 000 и 2 документами — в «Обычные».

Вложенный ВЫБОР: когда он нужен

Внутри ТОГДА и ИНАЧЕ может стоять ещё один ВЫБОР. Глубина вложенности не ограничена, но читать такой текст быстро становится трудно.

ВЫБОР
    КОГДА Документы.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг ТОГДА
        ВЫБОР
            КОГДА Документы.Сумма > 100000 ТОГДА "Крупная отгрузка"
            ИНАЧЕ "Отгрузка"
        КОНЕЦ
    ИНАЧЕ "Прочее"
КОНЕЦ КАК Вид

Как развернуть вложенность в плоский список

Почти любой вложенный ВЫБОР переписывается одним уровнем — за счёт порядка веток и условия через И:

ВЫБОР
    КОГДА Документы.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
        И Документы.Сумма > 100000 ТОГДА "Крупная отгрузка"
    КОГДА Документы.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
        ТОГДА "Отгрузка"
    ИНАЧЕ "Прочее"
КОНЕЦ КАК Вид

Результат тот же. Плоский вариант проще читать и проще дополнять новой веткой.

Вложенность оправдана в двух случаях:

  • 📌 Внешнее условие тяжёлое — например, длинный список типов, — и повторять его в каждой ветке неудобно.
  • 📌 Внутренний ВЫБОР стоит внутри арифметики: Сумма * ВЫБОР … КОНЕЦ.

ВЫБОР внутри агрегатных функций

Это приём, который заменяет несколько запросов одним. Нужны приход и расход в разных колонках — оборачиваете ВЫБОР в СУММА:

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

Регистр в примере условный, подставьте свой. Смысл приёма: одна выборка, одна группировка, две колонки. То же работает с КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВЫБОР …) — если в ИНАЧЕ поставить NULL, эти строки не посчитаются.

ВЫБОР, КОГДА и другие способы задать условие

Путаница «ВЫБОР или КОГДА» обычно идёт из встроенного языка. Там есть тернарный оператор ?(Условие, А, Б) и конструкция Если … Тогда … КонецЕсли. В тексте запроса их нет. Вот как соотносятся инструменты:

ИнструментГде работаетЧто делаетОграничение
ВЫБОР КОГДА … ТОГДА … КОНЕЦЯзык запросовЛюбое ветвлениеМногословен
КОГДАТолько внутри ВЫБОРНачинает веткуСам по себе не используется
ЕСТЬNULL(Поле, Замена)Язык запросовПодменяет NULLТолько одна проверка — на NULL
?(Условие, А, Б)Встроенный языкТернарный выборВ запросе вызывает синтаксическую ошибку
Выбор Когда … Конец в вычисляемом полеСКДВетвление в выражениях схемыРаботает над результатом, не над таблицами базы
Условие в ГДЕЯзык запросовОтбрасывает строкиНе меняет значения, только фильтрует

ЕСТЬNULL или ВЫБОР

Для замены NULL короче писать ЕСТЬNULL(Остатки.КоличествоОстаток, 0). Это то же самое, что ВЫБОР с проверкой ЕСТЬ NULL, но в одну строку. ВЫБОР нужен, когда подмена зависит от других полей или значений больше двух.

Фильтр или ветвление

Если строки с нулевой суммой вам просто не нужны — пишите ГДЕ Сумма <> 0. ВЫБОР в этом случае лишний. Он нужен, когда строку надо оставить, но показать в ней другое значение.

Проверьте себя
Что вернёт ВЫБОР без ИНАЧЕ, если ни одно условие не выполнилось?
Как правильно проверить в ветке, что поле пустое после левого соединения?
Сработают две ветки: КОГДА Кол < 10 и КОГДА Кол = 0. Количество равно 0. Что вернёт ВЫБОР?

Типичные ошибки в условиях запроса и как их отлаживать

Синтаксические

  • ❌ Забыли КОНЕЦ. Платформа сообщит о синтаксической ошибке и укажет место, обычно на следующем после ВЫБОР ключевом слове. Ищите незакрытую конструкцию выше.
  • ❌ Пропустили ТОГДА или поставили запятую между ветками. Ветки разделяются только словом КОГДА, запятые между ними не нужны.
  • ❌ Написали ?() или Если из встроенного языка. В запросе их нет.
  • ❌ Двойные кавычки внутри строки запроса во встроенном языке. Строковые константы в тексте запроса, который вы собираете в коде, пишутся удвоенными кавычками: ""Мало"".

Логические — запрос выполняется, но результат не тот

  • ⚠️ Сравнение = NULL вместо ЕСТЬ NULL. Ветка просто не срабатывает.
  • ⚠️ Широкое условие стоит выше узкого. Узкая ветка не получает ни одной строки.
  • ⚠️ Нет ИНАЧЕ. В числовой колонке появляются NULL, а итоги и последующая арифметика дают NULL — любое действие с NULL возвращает NULL.
  • ⚠️ Разные типы в ветках. Если одна ветка возвращает число, а другая строку, колонка получит составной тип. Дальше это ломает сортировку и сравнения.

Ошибки группировки

Поле с ВЫБОР в запросе с СГРУППИРОВАТЬ ПО должно либо целиком стоять внутри агрегатной функции, либо повторяться в группировке тем же выражением. Иначе платформа сообщит, что поле не входит в группу. Решения два: перенести ВЫБОР внутрь СУММА(), МАКСИМУМ() и других агрегатных функций, или скопировать выражение в СГРУППИРОВАТЬ ПО без псевдонима.

Деление на ноль

Расчёт средней цены Сумма / Количество падает на строке с нулевым количеством. Ветка спасает:

ВЫБОР
    КОГДА Итоги.Количество = 0 ТОГДА 0
    ИНАЧЕ Итоги.Сумма / Итоги.Количество
КОНЕЦ КАК СредняяЦена

Строки неограниченной длины

Реквизит строки без ограничения длины (например, комментарий) нельзя сравнивать напрямую — ни в ГДЕ, ни в КОГДА. Обрежьте значение: КОГДА ПОДСТРОКА(Док.Комментарий, 1, 100) = "срочно". Для поиска по части текста ПОДОБНО с такой обрезкой тоже работает.

Как отлаживать

  1. Выполните запрос в консоли запросов — внешней обработке, которую используют разработчики и администраторы. Там видна каждая строка результата.
  2. Вынесите условие каждой ветки отдельной колонкой: Товары.Количество < 10 КАК Проверка1. Сразу видно, какое условие истинно для конкретной строки.
  3. Для полей составного типа добавьте колонку ТИПЗНАЧЕНИЯ(Поле) — станет понятно, почему не срабатывает ССЫЛКА.
  4. Если подозреваете NULL — добавьте колонку Поле ЕСТЬ NULL.
  5. Когда ветки работают — уберите отладочные колонки.

В конструкторе запроса произвольное выражение вводится в редакторе поля. Длинные ВЫБОР удобнее править прямо в тексте запроса, а конструктор использовать для таблиц и соединений.

Оптимизация: как ВЫБОР влияет на скорость запроса

В списке полей ВЫБОР почти ничего не стоит: условие вычисляется над строками, которые и так уже выбраны. Проблемы начинаются в трёх местах.

ВЫБОР в ГДЕ и в условии соединения

Условие вида ГДЕ ВЫБОР КОГДА … КОНЕЦ = 1 СУБД не может разложить на индекс. Она вычисляет выражение для каждой строки таблицы — то есть читает её целиком. На регистре в миллионы записей это заметно.

Что делать:

  • ✅ Переписать условие обычной логикой: ГДЕ (А И Б) ИЛИ (НЕ А И В) вместо ВЫБОР.
  • ✅ Если ИЛИ всё равно мешает индексу — разбить запрос на две части через ОБЪЕДИНИТЬ ВСЕ, у каждой своё простое условие.
  • ✅ Отбор по измерениям регистра переносить в параметры виртуальной таблицы — Остатки(&Дата, Склад = &Склад). Там он применяется до расчёта.

Обращение через точку к полю составного типа

ВЫБОР КОГДА Движения.Регистратор.Контрагент = &Контрагент … выглядит невинно. Но если регистратор может быть документом десяти типов, платформа добавит в запрос соединения со всеми десятью таблицами. Уточните тип заранее:

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

С ВЫРАЗИТЬ соединение будет одно — с таблицей нужного документа.

Повторение одного и того же ВЫБОР

Если одно и то же длинное выражение нужно в полях, в группировке и в сортировке, вычислите его один раз во временной таблице. Следующий запрос пакета обращается к готовой колонке:

ВЫБРАТЬ
    Продажи.Контрагент,
    ВЫБОР КОГДА Продажи.Сумма >= 1000000 ТОГДА "Крупный" ИНАЧЕ "Обычный" КОНЕЦ КАК Сегмент,
    Продажи.Сумма
ПОМЕСТИТЬ ВТ_Сегменты
ИЗ
    &ТаблицаПродаж КАК Продажи
;
ВЫБРАТЬ
    ВТ.Сегмент,
    СУММА(ВТ.Сумма) КАК Сумма
ИЗ
    ВТ_Сегменты КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Сегмент

Здесь &ТаблицаПродаж — таблица значений, переданная параметром. Подставьте свой источник. Текст второго запроса становится короче, а ошибка в одном месте не расходится по трём.

Заменять пять обычных запросов одним с СУММА(ВЫБОР …) выгодно, когда источник один. Если ради одного запроса вы соединяете несвязанные таблицы, выигрыш пропадает: проще оставить отдельные запросы в пакете.

Частые вопросы

Можно ли использовать ВЫБОР в УПОРЯДОЧИТЬ ПО?
Да. Так задают свой порядок строк, например сначала просроченные: УПОРЯДОЧИТЬ ПО ВЫБОР КОГДА Срок < &Дата ТОГДА 0 ИНАЧЕ 1 КОНЕЦ, Срок.
Какой тип будет у колонки, если ветки возвращают строки разной длины?
Платформа возьмёт строку с длиной, достаточной для самого длинного значения. Если в ветках разные типы — число и строка — колонка станет составного типа.
Сколько веток КОГДА можно написать?
Жёсткого ограничения на практике нет. Если веток больше десятка и они сопоставляют значения, удобнее передать таблицу соответствий параметром и сделать соединение.
Работает ли ВЫБОР в вычисляемых полях СКД?
Да, в выражениях системы компоновки данных есть такая же конструкция Выбор Когда … Тогда … Иначе … Конец. Она вычисляется над результатом запроса, а не над таблицами базы.
Почему ВЫБОР возвращает NULL, хотя ИНАЧЕ указано?
Значит, NULL возвращает сама сработавшая ветка — обычно поле из левого соединения. Оберните его в ЕСТЬNULL().