Условные поля в запросах 1С 8.3: как устроена конструкция ВЫБОР КОГДА ТОГДА ИНАЧЕ КОНЕЦ

· 11 мин

Содержание

В языке запросов 1С условное поле пишется так: ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Это аналог CASE WHEN … THEN … ELSE … END из SQL. Ключевое слово именно ВЫБОР, а не «ВЫБРАТЬ»: ВЫБРАТЬ начинает сам запрос, и если написать «ВЫБРАТЬ КОГДА» внутри списка полей, платформа выдаст синтаксическую ошибку.

Конструкция работает в любой конфигурации на платформе 8.3 — в файловой и клиент-серверной базе, в консоли запросов, в конструкторе и в схеме компоновки данных.

Синтаксис ВЫБОР КОГДА ТОГДА: из чего состоит конструкция

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

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

Как это читается:

  • 🔹 КОГДА — логическое условие. Сравнение, МЕЖДУ, В, ПОДОБНО, ЕСТЬ NULL, ССЫЛКА, комбинации через И, ИЛИ, НЕ.
  • 🔹 ТОГДА — значение, которое получит поле, если условие истинно. Число, строка, дата, ссылка, булево, другое поле или выражение.
  • 🔹 ИНАЧЕ — значение, если не сработало ни одно условие. Необязательно, но без него в поле попадёт NULL.
  • 🔹 КОНЕЦ — обязательное закрытие. Без него запрос не выполнится.

Условия проверяются сверху вниз. Сработало первое истинное — остальные уже не смотрятся. Поэтому порядок веток имеет значение, особенно для диапазонов.

В языке запросов 1С есть только «поисковая» форма: после КОГДА всегда идёт полное условие. Короткая SQL-форма вида CASE Поле WHEN 1 THEN … здесь не работает — пишите КОГДА Поле = 1.

Простейший пример — подписать группы и элементы справочника:

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

Английский вариант синтаксиса тоже допустим: CASE WHEN … THEN … ELSE … END.

Где в запросе можно использовать оператор ВЫБОР

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

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

В СГРУППИРОВАТЬ ПО нельзя сослаться на псевдоним поля — повторите выражение ВЫБОР … КОНЕЦ целиком. В УПОРЯДОЧИТЬ ПО псевдоним использовать можно.

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

Примеры ВЫБОР КОГДА для чисел, дат, ссылок и NULL

Числа: разбивка по диапазонам

Классифицируем реализации по сумме:

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

Документ на 120 000 ₽ не проходит первую ветку, проходит вторую и получает «Средняя». Документ на 30 000 ₽ уходит в ИНАЧЕ.

Даты: просрочка в днях

Функция РАЗНОСТЬДАТ считает разницу между датами в нужных единицах. С ней удобно строить «возрастные» корзины:

ВЫБОР
    КОГДА РАЗНОСТЬДАТ(Док.Дата, &ДатаОтчета, ДЕНЬ) <= 30
        ТОГДА "до 30 дней"
    КОГДА РАЗНОСТЬДАТ(Док.Дата, &ДатаОтчета, ДЕНЬ) <= 90
        ТОГДА "31–90 дней"
    ИНАЧЕ "свыше 90 дней"
КОНЕЦ КАК Срок

Документ от 1 марта при дате отчёта 15 апреля даёт 45 дней — попадает во вторую корзину.

Ссылки и перечисления

Значения перечислений и предопределённых элементов в условии задаются через ЗНАЧЕНИЕ():

ВЫБОР
    КОГДА Контрагенты.ЮридическоеФизическоеЛицо = ЗНАЧЕНИЕ(Перечисление.ЮридическоеФизическоеЛицо.ФизическоеЛицо)
        ТОГДА "Физлицо"
    ИНАЧЕ "Организация"
КОНЕЦ КАК ТипКонтрагента

Имена реквизитов и перечислений здесь — из Бухгалтерии предприятия 3.0. В другой конфигурации проверьте их в дереве метаданных конфигуратора.

Для полей составного типа (например, Регистратор в регистре) тип проверяют через ССЫЛКА:

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

NULL после левого соединения

После ЛЕВОЕ СОЕДИНЕНИЕ в полях правой таблицы может оказаться NULL. Сравнение = NULL никогда не даёт истину, нужен ЕСТЬ NULL:

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

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

Условные суммы: «шахматка» по месяцам

ВЫБОР внутри агрегатной функции превращает строки в колонки:

ВЫБРАТЬ
    Реализация.Контрагент КАК Контрагент,
    СУММА(ВЫБОР КОГДА МЕСЯЦ(Реализация.Дата) = 1 ТОГДА Реализация.СуммаДокумента ИНАЧЕ 0 КОНЕЦ) КАК Январь,
    СУММА(ВЫБОР КОГДА МЕСЯЦ(Реализация.Дата) = 2 ТОГДА Реализация.СуммаДокумента ИНАЧЕ 0 КОНЕЦ) КАК Февраль,
    СУММА(ВЫБОР КОГДА МЕСЯЦ(Реализация.Дата) = 3 ТОГДА Реализация.СуммаДокумента ИНАЧЕ 0 КОНЕЦ) КАК Март
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Проведен
    И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    Реализация.Контрагент

ИНАЧЕ 0 здесь важен: без него в «пустых» месяцах вместо нуля будет NULL, и в выгрузке появятся пустые ячейки.

ВЫБОР в связке с МЕЖДУ, В и ПОДОБНО

МЕЖДУ: границы включаются

МЕЖДУ А И Б включает обе границы. Если диапазоны стыкуются, граничное значение уйдёт в первую подходящую ветку:

ВЫБОР
    КОГДА Сумма МЕЖДУ 0 И 10000 ТОГДА "до 10 тыс."
    КОГДА Сумма МЕЖДУ 10000 И 50000 ТОГДА "10–50 тыс."
    ИНАЧЕ "свыше 50 тыс."
КОНЕЦ

Сумма ровно 10 000 попадёт в «до 10 тыс.», хотя по смыслу могла бы во вторую группу. Чтобы не гадать, используйте пары >= и <: КОГДА Сумма >= 10000 И Сумма < 50000.

С датами та же ловушка: Дата МЕЖДУ &Начало И &Конец, где &Конец — дата без времени, отрежет документы, созданные днём последнего числа. Передавайте конец периода через КонецДня() или КонецМесяца().

В: проверка по списку

ВЫБОР
    КОГДА Реализация.Контрагент В (&КлючевыеКлиенты)
        ТОГДА "Ключевой"
    ИНАЧЕ "Обычный"
КОНЕЦ КАК Статус

В параметр &КлючевыеКлиенты передают массив или список значений. Короткий фиксированный список можно написать прямо в тексте: КОГДА МЕСЯЦ(Дата) В (1, 2, 3) ТОГДА "I квартал".

Если список большой и живёт в справочнике или регистре, не тащите его в В. Соедините таблицы и проверяйте поле присоединённой таблицы на ЕСТЬ NULL — так запрос понятнее и стабильнее по скорости.

ПОДОБНО: классификация по тексту

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

Шаблоны: % — любое количество любых символов, _ — ровно один символ. Если в тексте нужно найти сам символ % или _, задайте спецсимвол: ПОДОБНО "%100!%%" СПЕЦСИМВОЛ "!".

Классификация по наименованию хрупкая: одна опечатка в карточке — и позиция уходит в «Товары». Для отчётов, на которые смотрит руководство, лучше завести реквизит или группу и проверять их.

Отчёт с классификацией клиентов: пример целиком

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

ВЫБРАТЬ
    Реализация.Контрагент КАК Контрагент,
    СУММА(Реализация.СуммаДокумента) КАК Выручка
ПОМЕСТИТЬ ВТ_Выручка
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Проведен
    И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
    Реализация.Контрагент
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Выручка.Контрагент КАК Контрагент,
    ВТ_Выручка.Выручка КАК Выручка,
    ВЫБОР
        КОГДА ВТ_Выручка.Выручка >= 1000000
            ТОГДА "A — ключевой"
        КОГДА ВТ_Выручка.Выручка >= 300000
            ТОГДА "B — стабильный"
        ИНАЧЕ "C — разовый"
    КОНЕЦ КАК Класс,
    ВЫБОР
        КОГДА ВТ_Выручка.Выручка >= 1000000
            ТОГДА 1
        КОГДА ВТ_Выручка.Выручка >= 300000
            ТОГДА 2
        ИНАЧЕ 3
    КОНЕЦ КАК ПорядокКласса
ИЗ
    ВТ_Выручка КАК ВТ_Выручка

УПОРЯДОЧИТЬ ПО
    ПорядокКласса,
    Выручка УБЫВ

Что здесь сделано осознанно:

  1. Агрегация отдельно от классификации. Класс зависит от итоговой суммы, а не от отдельного документа. Поэтому ВЫБОР стоит во втором запросе, по готовой выручке.
  2. Второе поле ПорядокКласса. Сортировка по строке «A — ключевой» сработает, пока названия начинаются с букв по алфавиту. Числовой порядок не сломается, если завтра классы переименуют.
  3. Пороги не дублируются в разных местах кода. Если порогов становится больше, вынесите их в параметры &ПорогA, &ПорогB — тогда их можно менять без правки текста запроса.

Пример: клиент с выручкой 1 250 000 ₽ получит класс A и номер 1, клиент с 420 000 ₽ — B и 2, клиент с 80 000 ₽ — C и 3.

Вызов из встроенного языка:

Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ДатаОтчета));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ДатаОтчета));

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    Сообщить(Строка(Выборка.Контрагент) + ": " + Выборка.Класс);
КонецЦикла;

Чтобы получить сводку «сколько клиентов в каждом классе», добавьте третий запрос к ВТ_Выручка с КОЛИЧЕСТВО(Контрагент) и группировкой — и повторите в СГРУППИРОВАТЬ ПО выражение ВЫБОР целиком.

Как ускорить запрос с ВЫБОР КОГДА

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

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

Пример переписывания условия из ГДЕ:

// Медленно: индекс по дате не используется
ГДЕ
    ВЫБОР
        КОГДА &ТолькоТекущийМесяц
            ТОГДА Док.Дата >= &НачалоМесяца
        ИНАЧЕ ИСТИНА
    КОНЕЦ

// Быстрее: то же условие обычной логикой
ГДЕ
    (НЕ &ТолькоТекущийМесяц ИЛИ Док.Дата >= &НачалоМесяца)

Ещё надёжнее — собрать текст запроса в коде и добавлять условие по дате только тогда, когда отбор действительно нужен.

Условия ВЫБОР в СКД и во встроенном языке

В схеме компоновки данных ВЫБОР работает в двух местах:

  • 📊 В тексте запроса набора данных — ровно как в обычном запросе.
  • 📊 В вычисляемых полях и выражениях ресурсов — язык выражений СКД понимает Выбор Когда … Тогда … Иначе … Конец.

Вычисляемое поле СКД считается уже после получения данных. Для классификации по итоговым суммам это удобно, но отбор по такому полю не уходит в СУБД. Если отбирать нужно по классу, лучше рассчитать его в запросе.

Во встроенном языке модулей конструкции ВЫБОР нет. Её роль играют:

Если Сумма >= 500000 Тогда
    Категория = "Крупная";
ИначеЕсли Сумма >= 50000 Тогда
    Категория = "Средняя";
Иначе
    Категория = "Мелкая";
КонецЕсли;

// Для двух вариантов — тернарный оператор
Вид = ?(ЭтоГруппа, "Группа", "Элемент");

Классификацию большого набора строк лучше делать в запросе. Обработка той же логики в цикле по выборке на десятках тысяч строк заметно медленнее.

Ошибки при работе с ВЫБОР КОГДА и как их найти

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

Ещё два тихих источника ошибок, на которые запрос не ругается:

  • ⚠️ Сравнение с NULL через «=». КОГДА Поле = NULL всегда ложно — используйте ЕСТЬ NULL.
  • ⚠️ Строковые литералы в результате. Если поле потом сравнивают в коде со строкой, а в запросе текст изменили («Крупная» → «Крупный клиент»), код молча перестанет находить совпадения. Для логики лучше возвращать числа или значения перечислений, а строки — только для показа.

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

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