Условные выражения в запросах 1С 8.3: как устроена конструкция ВЫБОР и где она подводит

· 9 мин

Содержание

ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ — условное выражение языка запросов 1С 8.3, аналог CASE WHEN … THEN … ELSE … END из SQL. Оно вычисляет значение поля прямо в запросе: подставляет текст вместо кода, раскладывает суммы по колонкам, задаёт свой порядок сортировки.

Конструкция работает только внутри текста запроса и в выражениях системы компоновки данных (СКД). В модулях встроенного языка её нет: там пишут Если … ИначеЕсли … Иначе … КонецЕсли или короткую форму ?(Условие, Значение1, Значение2).

Как записывается ВЫБОР КОГДА ТОГДА ИНАЧЕ КОНЕЦ

Полная форма выражения:

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

Английский вариант того же текста: CASE WHEN … THEN … ELSE … END. Платформа понимает оба написания; в типовых конфигурациях принято русское.

Правила, которые надо держать в голове:

  • 🔹 веток КОГДА может быть сколько угодно, но хотя бы одна обязательна;
  • 🔹 условия проверяются сверху вниз, срабатывает первая истинная ветка, остальные уже не смотрятся;
  • 🔹 ИНАЧЕ необязательна — без неё при всех ложных условиях выражение вернёт NULL;
  • 🔹 выражение всегда закрывается словом КОНЕЦ, даже вложенное;
  • 🔹 после КОНЕЦ полю почти всегда дают имя через КАК, иначе платформа назовёт колонку сама (Поле1, Поле2), и обращаться к ней из кода неудобно.

Простейший пример — показать признак пометки удаления словами:

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

В SQL есть короткая форма CASE Поле WHEN 1 THEN …, где поле пишут один раз. В языке запросов 1С пользуйтесь полной формой: условие целиком в каждой ветке (КОГДА Поле = 1 ТОГДА …). Так текст читается однозначно и его без ошибок разбирает конструктор запроса.

Где выражение допустимо

ВЫБОР — обычное выражение, поэтому его ставят везде, где запрос принимает выражение:

Часть запросаЗачем там ВЫБОР
ВЫБРАТЬвычисляемое поле: текст, число, признак
внутри СУММА, КОЛИЧЕСТВО, МАКСИМУМусловная агрегация, суммы по колонкам
ГДЕ и ИМЕЮЩИЕусловие, зависящее от другого поля или параметра
СГРУППИРОВАТЬ ПОгруппировка по вычисленной категории
УПОРЯДОЧИТЬ ПОнестандартный порядок строк
условие СОЕДИНЕНИЕ … ПОтехнически можно, но почти всегда дорого

В конструкторе запроса выражение вводят в окне произвольного выражения поля: на вкладке «Таблицы и поля» добавьте поле и вместо выбора реквизита напишите текст вручную.

ВЫБОР или ЕСЛИ: что брать в запросе и что в коде

Частый вопрос новичков звучит как «чем заменить ЕСЛИ в запросе». Ответ прямой: в языке запросов оператора ЕСЛИ нет, единственная условная конструкция — ВЫБОР. Обратное тоже верно: в модуле нельзя написать ВЫБОР КОГДА.

Где что применять:

ЗадачаИнструмент
значение зависит от полей таблицы, обрабатывается много строкВЫБОР в запросе
логика нужна в вычисляемом поле отчёта на СКДВЫБОР в выражении поля СКД
нужно вызвать функцию общего модуля, обратиться к объекту, записать данныеЕсли в коде
одно значение по одному условию в коде?(Условие, А, Б)
ветвление по многим условиям в кодеЕсли … ИначеЕсли … КонецЕсли

Главный довод в пользу запроса — объём данных. Посчитать категорию для 200 000 строк остатков на стороне СУБД заметно быстрее, чем выгрузить их в таблицу значений и перебрать циклом. А ещё запрос с ВЫБОР сразу отдаёт готовую колонку в отчёт: не нужен лишний проход по результату.

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

Сложные условия, параметры и вложенные ВЫБОР

В ветке КОГДА стоит любое логическое выражение. Можно комбинировать И, ИЛИ, НЕ, сравнения, МЕЖДУ, В (…), ПОДОБНО, ЕСТЬ NULL, ССЫЛКА.

Условие по типу ссылки

Поле Регистратор регистра ссылается на документы разных видов. ВЫБОР с оператором ССЫЛКА превращает его в понятную колонку:

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

Тот же результат даёт сравнение ТИПЗНАЧЕНИЯ(Поле) = ТИП(Документ.РеализацияТоваровУслуг). Имена документов в примерах — из типовых конфигураций; в своей базе подставьте те, что есть в дереве метаданных.

Условие по параметру

Параметр в условии работает как переключатель отчёта:

ВЫБОР
    КОГДА &ВВалюте
        ТОГДА Остатки.СуммаВалютнаяОстаток
    ИНАЧЕ Остатки.СуммаОстаток
КОНЕЦ КАК Сумма

Параметр ВВалюте передают из кода через Запрос.УстановитьПараметр("ВВалюте", Истина). Одна текстовка запроса обслуживает оба режима.

Вложенный ВЫБОР

Внутри ТОГДА и ИНАЧЕ допускается ещё один ВЫБОР. Каждое вложенное выражение закрывается своим КОНЕЦ:

ВЫБОР
    КОГДА Т.Сумма >= 1000000 ТОГДА
        ВЫБОР
            КОГДА Т.Просрочено ТОГДА "Крупный, просрочен"
            ИНАЧЕ "Крупный"
        КОНЕЦ
    ИНАЧЕ "Обычный"
КОНЕЦ КАК Группа

Больше двух уровней вложенности читать уже тяжело. Чаще всего вложенность разворачивается в плоский список веток с условием через И — порядок веток сделает ту же работу.

Типичные ошибки в конструкции ВЫБОР

Диагностика: выберите симптом

Отдельная ловушка — типы. Если ветки возвращают значения разных типов (строку в одной, число в другой), колонка получает составной тип. Отчёт это переживёт, но сортировка и сравнения дальше по запросу начнут вести себя непредсказуемо. Держите все ветки в одном типе, а при необходимости явно приводите через ВЫРАЗИТЬ.

Ещё одна — пустые ссылки. Пустой контрагент в документе и отсутствие строки в левом соединении — разные вещи. Первое ловится сравнением со ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка), второе — через ЕСТЬ NULL. Если нужны оба случая, пишите обе проверки в одной ветке через ИЛИ.

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

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

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

Классический пример «универсального» отбора:

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

Работать будет, но индекс по складу, скорее всего, не используется — даже когда склад задан. На маленькой базе разницы не видно, на регистре с миллионами записей запрос заметно тормозит.

Что сделать вместо этого:

  1. Собрать текст запроса в коде: добавлять строку И Остатки.Склад = &Склад только когда склад выбран.
  2. В отчёте на СКД вынести отбор в настройки — компоновщик сам добавит условие, только если пользователь его включил.
  3. Для виртуальных таблиц остатков и оборотов ставить условие в параметры таблицы, а не в ГДЕ.

В условии соединения ВЫБОР — самый дорогой вариант. Если ключ соединения зависит от условия, лучше разбить запрос на две части и склеить через ОБЪЕДИНИТЬ ВСЕ, либо подготовить ключ во временной таблице и проиндексировать её.

Временная таблица вообще хорошо снимает нагрузку: посчитайте категорию один раз, положите в ПОМЕСТИТЬ, добавьте ИНДЕКСИРОВАТЬ ПО и соединяйтесь уже по готовому полю. Сравнивать варианты стоит на реальных данных — замером времени или в технологическом журнале, а не на глаз.

Когда лучше обойтись без ВЫБОР

ВЫБОР — не единственный инструмент, и иногда он только мешает.

ЗадачаВместо ВЫБОРПочему
заменить NULL на значениеЕСТЬNULL(Поле, 0)короче и понятнее
показать имя ссылкиПРЕДСТАВЛЕНИЕ(Поле)платформа сама возьмёт представление
перевести коды в названия, вариантов 20+справочник или временная таблица с соединениемсписок веток неудобно поддерживать
разные отборы под разные режимысобрать текст запроса в кодеиндексы работают
разные ключи соединенияОБЪЕДИНИТЬ ВСЕ двух запросовкаждый запрос простой и быстрый
логика с вызовом функцийобработка результата в кодефункции модулей в запросе недоступны

Простое правило: если ветки ВЫБОР описывают справочные данные (коды, ставки, соответствия), им место в таблице, а не в тексте запроса. Завтра добавится новое значение, и править придётся код вместо одной записи в справочнике.

Примеры ВЫБОР КОГДА ИНАЧЕ для типовых задач

Приход и расход из одного регистра

Регистр накопления хранит движения с видом «Приход» или «Расход». Разложить их по двум колонкам:

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

Имя регистра условное — подставьте свой. Для готовых оборотов проще взять виртуальную таблицу .Обороты, где поля прихода и расхода уже есть; приём с условной суммой нужен, когда считаете по физической таблице или по своим признакам.

Возраст задолженности

Разбить долг по срокам относительно даты отчёта:

ВЫБОР
    КОГДА РАЗНОСТЬДАТ(Долги.ДатаДокумента, &ДатаОтчета, ДЕНЬ) <= 30
        ТОГДА "до 30 дней"
    КОГДА РАЗНОСТЬДАТ(Долги.ДатаДокумента, &ДатаОтчета, ДЕНЬ) <= 60
        ТОГДА "31–60 дней"
    КОГДА РАЗНОСТЬДАТ(Долги.ДатаДокумента, &ДатаОтчета, ДЕНЬ) <= 90
        ТОГДА "61–90 дней"
    ИНАЧЕ "больше 90 дней"
КОНЕЦ КАК Интервал

Здесь работает порядок веток. Документ от 15 января при дате отчёта 1 марта даёт 45 дней: первая ветка ложна, вторая истинна — строка уходит в «31–60 дней». Нижнюю границу в каждой ветке писать не нужно.

Свой порядок сортировки

Показать сначала просроченные заказы, затем текущие, в конце закрытые:

УПОРЯДОЧИТЬ ПО
    ВЫБОР
        КОГДА Заказы.Просрочен ТОГДА 1
        КОГДА НЕ Заказы.Закрыт ТОГДА 2
        ИНАЧЕ 3
    КОНЕЦ,
    Заказы.Дата

Поля Просрочен и Закрыт условные: в вашей таблице это могут быть статус или дата закрытия. Суть приёма — числовой ключ сортировки, которого в данных нет.

Защита от деления на ноль

Цена за единицу при нулевом количестве:

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

Без этой проверки запрос на строке с нулевым количеством упадёт с ошибкой деления на ноль, и отчёт не сформируется целиком.

Флажок для отбора в отчёте

ВЫБОР может возвращать ИСТИНА или ЛОЖЬ. Колонка-признак удобна в СКД: пользователь ставит отбор «ЕстьОстатокДолга = Да» в настройках, не разбираясь в суммах.

ВЫБОР
    КОГДА Расчеты.СуммаОстаток > 0 ТОГДА ИСТИНА
    ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК ЕстьОстатокДолга

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

Частые вопросы про ВЫБОР в запросах 1С

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