ВЫБОР КОГДА … ИНАЧЕ … КОНЕЦ: условные поля в языке запросов 1С
Содержание
В языке запросов 1С «Когда Иначе» записывается так: ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Условия проверяются сверху вниз, срабатывает первое истинное, а если ни одно не подошло — берётся значение после ИНАЧЕ. Нет ИНАЧЕ — результат будет NULL.
Это аналог CASE WHEN … THEN … ELSE … END из SQL, и в английском синтаксисе 1С он пишется именно так. Оператор работает в списке полей, в ГДЕ, УПОРЯДОЧИТЬ ПО, ИМЕЮЩИЕ, в условиях соединения и внутри агрегатных функций. Синтаксис одинаков для любой конфигурации на платформе 8.3: БП, ЗУП, УТ, ERP или своей разработки.
Синтаксис ВЫБОР КОГДА ТОГДА ИНАЧЕ КОНЕЦ
Полная форма оператора:
ВЫБОР
КОГДА <условие 1> ТОГДА <выражение 1>
КОГДА <условие 2> ТОГДА <выражение 2>
ИНАЧЕ <выражение по умолчанию>
КОНЕЦ КАК ИмяПоля
Что из чего состоит:
- 🔹
ВЫБОРоткрывает конструкцию,КОНЕЦзакрывает. ЗабытыйКОНЕЦ— самая частая синтаксическая ошибка. - 🔹
КОГДА … ТОГДА— пара «условие → результат». Пар может быть сколько угодно, но хотя бы одна обязательна. - 🔹
ИНАЧЕ— необязательная ветка по умолчанию. - 🔹
КАК ИмяПоля— псевдоним. Без него платформа назовёт поле сама, и обращаться к нему из кода будет неудобно.
Простой пример: разбить номенклатуру на услуги и товары.
ВЫБРАТЬ
Номенклатура.Наименование КАК Наименование,
ВЫБОР
КОГДА Номенклатура.Услуга ТОГДА "Услуга"
ИНАЧЕ "Товар"
КОНЕЦ КАК Вид
ИЗ
Справочник.Номенклатура КАК Номенклатура
В 1С есть только «поисковая» форма оператора — с полноценным условием после каждого КОГДА. Короткая запись из SQL вида ВЫБОР Поле КОГДА 1 ТОГДА … не поддерживается. Сравнение пишут явно: КОГДА Поле = 1 ТОГДА ….
После КОГДА допустимо любое логическое выражение языка запросов: сравнения, И, ИЛИ, НЕ, В (…), МЕЖДУ, ПОДОБНО, ЕСТЬ NULL, ССЫЛКА, а также параметры &Имя.
Как вставить конструкцию в текст запроса из кода
В модуле текст запроса — это строка. Строковые литералы внутри неё пишут в удвоенных кавычках:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура,
| ВЫБОР
| КОГДА Товары.Сумма >= &Порог ТОГДА ""Крупная""
| ИНАЧЕ ""Обычная""
| КОНЕЦ КАК КатегорияСтроки
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары";
Запрос.УстановитьПараметр("Порог", 100000);
В конструкторе запроса такое поле добавляют как произвольное выражение: открывают редактор выражения поля и вписывают ВЫБОР … КОНЕЦ целиком. Конструктор его сохранит и потом откроет без потерь.
Несколько условий КОГДА и вложенный ВЫБОР
Когда вариантов больше двух, просто добавляйте строки КОГДА. Порядок решает всё: платформа остановится на первом истинном условии.
ВЫБОР
КОГДА Остатки.КоличествоОстаток <= 0 ТОГДА "Нет в наличии"
КОГДА Остатки.КоличествоОстаток < 10 ТОГДА "Мало"
КОГДА Остатки.КоличествоОстаток < 100 ТОГДА "Достаточно"
ИНАЧЕ "Много"
КОНЕЦ КАК УровеньЗапаса
Проверим на числах. Остаток 5: первое условие ложно, второе истинно — «Мало». Остаток 50 — «Достаточно». Остаток 250 — «Много». Если поменять местами строки «< 100» и «< 10», остаток 5 попадёт в «Достаточно», и ветка «Мало» не сработает никогда.
Ставьте узкие условия выше широких. Иначе часть веток станет мёртвым кодом, а ошибку не покажет ни синтаксис-контроль, ни конструктор.
Комбинированные условия
В одном КОГДА можно проверять несколько полей сразу:
ВЫБОР
КОГДА Продажи.Сумма > 500000 И Продажи.Контрагент.ЮрФизЛицо = ЗНАЧЕНИЕ(Перечисление.ЮрФизЛицо.ЮрЛицо)
ТОГДА "Ключевой клиент"
КОГДА Продажи.Сумма > 500000
ТОГДА "Крупный покупатель"
ИНАЧЕ "Розница"
КОНЕЦ
Предопределённые значения и перечисления в условиях берут через ЗНАЧЕНИЕ(…). Строку вроде "ЮрЛицо" с перечислением сравнивать бесполезно — условие не выполнится.
Вложенный ВЫБОР
Внутри ТОГДА или ИНАЧЕ может стоять ещё один ВЫБОР. Это удобно, когда вторая проверка нужна только в одной ветке:
ВЫБОР
КОГДА Документ.Проведен ТОГДА
ВЫБОР
КОГДА Документ.СуммаДокумента = 0 ТОГДА "Проведён, пустой"
ИНАЧЕ "Проведён"
КОНЕЦ
ИНАЧЕ "Черновик"
КОНЕЦ КАК Состояние
Каждый вложенный ВЫБОР требует своего КОНЕЦ. Больше двух уровней вложенности читать тяжело — обычно такую логику можно развернуть в плоский список КОГДА с условиями через И.
«Когда Иначе» внутри СУММА, КОЛИЧЕСТВО и других агрегатов
Самый полезный приём — условная агрегация. Один проход по таблице даёт несколько итогов в разных колонках, без подзапросов и ОБЪЕДИНИТЬ.
ВЫБРАТЬ
Движения.Номенклатура,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Поступило,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Списано
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
Имя регистра здесь условное — подставьте регистр своей конфигурации. Пример: у товара приход 30 и 20 штук, расход 15. Получим строку «Поступило 50, Списано 15».
ИНАЧЕ 0 или без ИНАЧЕ
Для СУММА разница почти незаметна: агрегатные функции пропускают NULL. Но есть нюанс. Если ни одна строка группы не попала в ветку, СУММА без ИНАЧЕ 0 вернёт NULL, а не ноль. В отчёте будет пустая ячейка, а в коде — сравнение с NULL, которое ломает арифметику.
С КОЛИЧЕСТВО всё наоборот — там ИНАЧЕ обычно мешает:
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВЫБОР
КОГДА Продажи.Сумма > 100000 ТОГДА Продажи.Контрагент
КОНЕЦ) КАК КрупныхПокупателей
Строки, не прошедшие условие, дают NULL, и КОЛИЧЕСТВО их не считает. Если дописать ИНАЧЕ 0, ноль станет ещё одним «различным» значением и прибавит единицу к результату.
Группировка по полю с ВЫБОР
Если поле с ВЫБОР стоит в списке выборки без агрегата, а в запросе есть СГРУППИРОВАТЬ ПО, платформа требует, чтобы его источник входил в группировку. Иначе получите ошибку вида «Поле не входит в группу».
Два рабочих варианта:
- Сгруппировать по исходным полям, которые участвуют в условии, например
СГРУППИРОВАТЬ ПО Остатки.Номенклатура, Остатки.КоличествоОстаток. - Повторить в
СГРУППИРОВАТЬ ПОвыражениеВЫБОР … КОНЕЦцеликом, символ в символ. Псевдоним изВЫБРАТЬв группировке не работает .
Если нужен итог именно по категориям («сколько продаж в каждой категории»), удобнее посчитать категорию во временной таблице, а сгруппировать уже во втором запросе пакета по готовому полю.
Где ещё работает конструкция: таблица вариантов
| Часть запроса | Для чего | Пример |
|---|---|---|
ВЫБРАТЬ | Вычисляемое поле, подпись, флаг | ВЫБОР КОГДА Сумма > 0 ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК ЕстьСумма |
| Агрегатная функция | Итоги по условию в отдельных колонках | СУММА(ВЫБОР КОГДА … ТОГДА Сумма ИНАЧЕ 0 КОНЕЦ) |
ГДЕ | Условие, зависящее от параметра | ГДЕ ВЫБОР КОГДА &ВсеСклады ТОГДА ИСТИНА ИНАЧЕ Склад = &Склад КОНЕЦ |
УПОРЯДОЧИТЬ ПО | Свой порядок сортировки | УПОРЯДОЧИТЬ ПО ВЫБОР КОГДА Статус = &Срочный ТОГДА 1 ИНАЧЕ 2 КОНЕЦ |
ИМЕЮЩИЕ | Фильтр по итогу с условием | ИМЕЮЩИЕ СУММА(ВЫБОР КОГДА … ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) > 3 |
СГРУППИРОВАТЬ ПО | Группировка по вычисленной категории | Выражение ВЫБОР … КОНЕЦ целиком |
Условие СОЕДИНЕНИЕ | Соединение по разным полям в зависимости от типа | Работает, но медленно — см. раздел про оптимизацию |
Флаг Истина/Ложь в выборке
Логическое выражение вроде Сумма > 0 нельзя поставить в список полей напрямую. Стандартный приём — обернуть его в ВЫБОР:
ВЫБОР КОГДА Товары.Сумма > 0 ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК ЕстьСумма
Поле получит тип «Булево», и в коде с ним можно работать как с обычным флагом.
Своя сортировка
Сортировать по алфавиту или по дате умеет любой запрос. А если «Срочные» нужны вверху, «Обычные» — в середине, а «Отложенные» — внизу, помогает ВЫБОР с числовыми весами 1, 2, 3 в УПОРЯДОЧИТЬ ПО. Поле с весом даже не обязательно выводить в результат.
NULL, ссылки и тип результата
Проверка на NULL
NULL появляется после левых соединений и при обращении к реквизитам через точку у пустой ссылки. Сравнение Поле = NULL никогда не бывает истинным — правильно писать Поле ЕСТЬ NULL:
ВЫБОР
КОГДА Цены.Цена ЕСТЬ NULL ТОГДА 0
ИНАЧЕ Цены.Цена
КОНЕЦ КАК Цена
Для такой простой подстановки короче функция ЕСТЬNULL(Цены.Цена, 0). Результат тот же, текст запроса чище. ВЫБОР нужен, когда при NULL надо вернуть не константу, а другое поле или выполнить ещё проверку.
Любое условие с участием NULL (кроме ЕСТЬ NULL) считается невыполненным. Строка с пустой ценой пройдёт мимо КОГДА Цена > 100 и мимо КОГДА Цена <= 100, а попадёт в ИНАЧЕ. Об этом легко забыть, когда кажется, что условия «покрывают все случаи».
Составные типы и оператор ССЫЛКА
Поле Регистратор или реквизит составного типа удобно разбирать по видам документов:
ВЫБОР
КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА "Поступление"
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА "Реализация"
ИНАЧЕ "Прочее"
КОНЕЦ КАК ВидОперации
Чтобы достать реквизит конкретного вида, добавьте ВЫРАЗИТЬ: ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент. Без него платформа соединит таблицы всех возможных типов регистратора, а это ощутимо медленнее.
Какой тип вернёт ВЫБОР
Тип поля складывается из типов всех веток. Если в одной ветке строка, а в другой число, поле станет составным «Строка, Число». С таким полем неудобно работать: его нельзя сразу просуммировать, сортировка и сравнение ведут себя неочевидно.
- ✅ Возвращайте во всех ветках один тип: везде числа или везде строки.
- ✅ Если ветка должна дать «пусто», используйте пустое значение того же типа:
0,"",ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка). - ✅ Для строк задайте длину явно, если результат потом сравнивается или участвует в группировке:
ВЫРАЗИТЬ(ВЫБОР … КОНЕЦ КАК СТРОКА(50)).
Строки неограниченной длины нельзя сравнивать, группировать и использовать в РАЗЛИЧНЫЕ. Если в ветку ТОГДА попадает реквизит типа «Строка» без ограничения длины (например, комментарий), оберните его в ВЫРАЗИТЬ(… КАК СТРОКА(N)).
Скорость: когда ВЫБОР тормозит запрос и чем его заменить
В списке полей ВЫБОР почти ничего не стоит: СУБД считает выражение для уже отобранных строк. Проблемы начинаются, когда он попадает в отбор или соединение.
ВЫБОР в ГДЕ и в условии соединения
Условие вида ГДЕ ВЫБОР КОГДА … КОНЕЦ = … СУБД не может разложить на индексный поиск. Она вычисляет выражение для каждой строки таблицы, то есть читает таблицу целиком. На справочнике в 500 элементов этого не видно. На регистре в несколько миллионов записей запрос вырастает с долей секунды до минут.
Что делать вместо этого:
| Задача | Вместо ВЫБОР | Почему лучше |
|---|---|---|
| Отбор «все склады или один» | Два варианта текста запроса, собранные в коде, или ГДЕ (&ВсеСклады ИЛИ Склад = &Склад) | Условие проще, оптимизатору понятнее |
| Соединение по разным полям в зависимости от типа | Два запроса с ОБЪЕДИНИТЬ ВСЕ, в каждом своё простое соединение | Каждое соединение идёт по индексу |
Подстановка значения вместо NULL | ЕСТЬNULL(Поле, 0) | Короче, тот же результат |
| Десятки веток «код → название» | Временная таблица соответствий и соединение с ней | Справочник правил можно менять без правки запроса |
| Отбор по периоду виртуальной таблицы | Параметры виртуальной таблицы (&Период, Условие) | Отбор выполнится до расчёта остатков, а не после |
Чего не стоит делать
- ❌ Вычислять в запросе
ВЫБОРпо полю, которое затем стоит вГДЕтого же запроса. Сначала отберите строки по исходному полю, потом классифицируйте. - ❌ Ставить
ВЫБОРв условия виртуальной таблицы регистра. Там нужны простые условия на измерения. - ❌ Разворачивать в запросе логику, которую проще посчитать в СКД. В системе компоновки данных
ВЫБОРтоже доступен в вычисляемых полях и выражениях ресурсов, и считается уже над результатом.
Чтобы проверить, мешает ли ВЫБОР, сравните время выполнения запроса в консоли запросов с конструкцией в ГДЕ и с переписанным условием на реальном объёме данных. Тестовая база с сотней документов разницы не покажет.