Конструкция ВЫБОР КОГДА ТОГДА в запросе 1С: как писать условия
Содержание
В языке запросов 1С условие «если — то — иначе» записывается так: ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Это выражение, а не оператор: оно возвращает одно значение и стоит там, где допустимо поле, — в списке выбираемых полей, в ГДЕ, в УПОРЯДОЧИТЬ ПО, в условии соединения.
Слова «Когда» и «Тогда» здесь не отдельные команды, а части конструкции ВЫБОР. Без КОНЕЦ запрос не скомпилируется. Без ИНАЧЕ результат для непопавших строк будет NULL, и это источник половины ошибок. Ниже — по порядку: от синтаксиса до соединений и ловушек.
Синтаксис: из чего состоит выражение
Минимальная форма:
ВЫБРАТЬ
Номенклатура.Наименование КАК Наименование,
ВЫБОР
КОГДА Номенклатура.Услуга
ТОГДА "Услуга"
ИНАЧЕ "Товар"
КОНЕЦ КАК Вид
ИЗ
Справочник.Номенклатура КАК Номенклатура
Разберём части:
ВЫБОР— открывает выражение.КОГДА— после него идёт логическое условие: сравнение,И,ИЛИ,НЕ,ЕСТЬ NULL,В (...),ПОДОБНО.ТОГДА— значение, которое вернётся, если условие истинно.ИНАЧЕ— значение «по умолчанию». Необязательная часть.КОНЕЦ— закрывает выражение. Обязателен.
Ветвей КОГДА ... ТОГДА может быть сколько угодно:
ВЫБОР
КОГДА Продажи.Сумма >= 100000 ТОГДА "Крупная"
КОГДА Продажи.Сумма >= 10000 ТОГДА "Средняя"
ИНАЧЕ "Мелкая"
КОНЕЦ КАК Категория
Для результата выражения почти всегда стоит давать псевдоним через КАК. Без него поле получит служебное имя, и в обработке результата обращаться к нему неудобно.
Порядок проверки: кто выигрывает при пересечении условий
Ветви проверяются сверху вниз. Первая сработавшая побеждает, остальные не смотрятся вообще. Отсюда правило: сначала узкие и строгие условия, потом широкие.
Посмотрите на пример с суммой выше. Продажа на 150 000 удовлетворяет и первому, и второму условию, но получит «Крупная», потому что первое стоит выше. Поменяйте ветви местами — и все продажи от 10 000 станут «Средними», а «Крупная» не получит ни одной строки.
Типичный сбой выглядит так:
| Порядок ветвей | Продажа 150 000 | Продажа 15 000 | Продажа 500 |
|---|---|---|---|
Сначала >= 100000, потом >= 10000 | Крупная | Средняя | Мелкая |
Сначала >= 10000, потом >= 100000 | Средняя | Средняя | Мелкая |
Если условия не пересекаются (например, проверка равенства разным кодам), порядок ни на что не влияет. Но привычка идти от частного к общему всё равно спасает при правках запроса через полгода.
Что вернётся, если ничего не подошло
- есть
ИНАЧЕ— значение изИНАЧЕ; ИНАЧЕнет —NULL.
И второе значение вы не увидите в отладчике как «пусто»: NULL не равен ни нулю, ни пустой строке, ни пустой ссылке. Если дальше в запросе поле участвует в арифметике, результат тоже станет NULL.
Выбор значения в списке выбираемых полей
Это самое частое применение. Несколько рабочих шаблонов.
Подпись вместо кода или булева значения
ВЫБОР
КОГДА Договоры.ПометкаУдаления ТОГДА "Помечен на удаление"
КОГДА Договоры.ДатаОкончания < &ТекущаяДата ТОГДА "Просрочен"
ИНАЧЕ "Действует"
КОНЕЦ КАК Состояние
Здесь порядок важен: договор, помеченный на удаление и просроченный одновременно, получит первую подпись.
Условная сумма внутри агрегатной функции
Приём, который выручает чаще всего: посчитать за один проход по таблице несколько разных сумм.
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
СУММА(ВЫБОР
КОГДА Продажи.Период >= &НачалоМесяца
ТОГДА Продажи.Сумма
ИНАЧЕ 0
КОНЕЦ) КАК СуммаЗаМесяц,
СУММА(Продажи.Сумма) КАК СуммаВсего
ИЗ
РегистрНакопления.Продажи КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Контрагент
Здесь ИНАЧЕ 0 обязателен. Без него для старых периодов выражение вернёт NULL. СУММА пропускает NULL, так что итог сойдётся, но если у контрагента вообще не было строк за месяц, придёт NULL вместо нуля, а при следующей операции над этим полем получите новый NULL.
То же работает для КОЛИЧЕСТВО: подсчёт строк с условием делается как СУММА(ВЫБОР КОГДА <условие> ТОГДА 1 ИНАЧЕ 0 КОНЕЦ).
Приведение типов результата
Ветви могут возвращать разные типы: число в одной, строка в другой, ссылка в третьей. Тогда поле получает составной тип, и дальше его неудобно сравнивать и выгружать. Если нужна определённость, приводите значение явно:
ВЫРАЗИТЬ(
ВЫБОР
КОГДА Строки.Количество = 0 ТОГДА 0
ИНАЧЕ Строки.Сумма / Строки.Количество
КОНЕЦ КАК ЧИСЛО(15, 2)) КАК Цена
Условие защищает от деления на ноль и пустых строк, а ВЫРАЗИТЬ фиксирует точность результата.
Строковые литералы в ветвях лучше тоже приводить: ВЫРАЗИТЬ("Да" КАК Строка(3)). Иначе поле получает строку неограниченной длины, а с такими полями ограничены группировка и сравнение.
Условия соединения таблиц
ВЫБОР допустим после ПО в соединении. Нужен он, когда ключ связи зависит от данных строки: в одних строках связываем по одному полю, в других — по другому.
ВЫБРАТЬ
Документы.Ссылка КАК Ссылка,
Контрагенты.Наименование КАК Наименование
ИЗ
Документ.РеализацияТоваровУслуг КАК Документы
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО Контрагенты.Ссылка = ВЫБОР
КОГДА Документы.Комиссия
ТОГДА Документы.Комитент
ИНАЧЕ Документы.Контрагент
КОНЕЦ
Работает, но с оговорками:
- Индексы. Если в условии соединения стоит вычисляемое выражение, СУБД часто не может использовать индекс по полю. На таблицах в сотни тысяч строк это заметно в скорости.
- Читаемость. Условие в
ПОдлиннее двух ветвей уже трудно читать. - Тип ключа. Все ветви выражения должны возвращать сопоставимые типы. Ссылку на контрагента нельзя «склеить» со строкой.
Альтернатива, которая обычно быстрее: разнести на два запроса во временные таблицы или сделать два левых соединения и выбрать результат уже в списке полей:
ВЫБОР
КОГДА Документы.Комиссия ТОГДА КонтрагентКомитент.Наименование
ИНАЧЕ КонтрагентОсновной.Наименование
КОНЕЦ КАК Наименование
Соединений станет два, зато каждое идёт по простому равенству.
Отдельная ловушка: условие в ПО и условие в ГДЕ для левого соединения — не одно и то же. Условие в ГДЕ по полю правой таблицы фактически превращает левое соединение во внутреннее, потому что строки с NULL отсекаются.
Вложенные условия и сложная логика
ВЫБОР можно вкладывать в ТОГДА, в ИНАЧЕ и даже в условие КОГДА.
ВЫБОР
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт)
ТОГДА "Завершён"
ИНАЧЕ
ВЫБОР
КОГДА Заказы.ДатаОтгрузки < &Сегодня ТОГДА "Просрочен"
КОГДА Заказы.Оплачен ТОГДА "К отгрузке"
ИНАЧЕ "Ждёт оплаты"
КОНЕЦ
КОНЕЦ КАК Этап
Вложение удобно, когда внутренняя логика имеет смысл только для части строк. Но у него есть предел: три уровня уже плохо читаются.
Что делать, если логика разрастается:
- Склеить условия через
И/ИЛИ. Две ветви с одинаковым результатом лучше объединить:КОГДА А ИЛИ Б ТОГДА Х. - Вынести часть вычислений во временную таблицу. Первым запросом посчитайте промежуточный признак, вторым используйте его в
ВЫБОР. - Использовать
В (...)вместо цепочки равенств:КОГДА Заказы.Статус В (ЗНАЧЕНИЕ(...), ЗНАЧЕНИЕ(...)) ТОГДА ....
Скобки и приоритет И и ИЛИ
Внутри условия И связывает сильнее, чем ИЛИ. Выражение А ИЛИ Б И В читается как А ИЛИ (Б И В). Если вы имели в виду (А ИЛИ Б) И В, поставьте скобки явно. Это та самая ошибка, из-за которой «в отчёте лишние строки» при вроде бы правильном условии.
Когда ВЫБОР не нужен
Не каждое «если» требует ВЫБОР. Три случая, где есть вариант проще:
- замена
NULLна значение —ЕСТЬNULL(Поле, 0); - фильтр по списку значений —
Поле В (...)вГДЕ; - постоянное соответствие «код → подпись» на десяток строк и больше — отдельный справочник или регистр сведений , подключённый соединением.
Последний вариант особенно выручает, когда подписи меняют без программиста: правится справочник, а не текст запроса.
Условие в ГДЕ, УПОРЯДОЧИТЬ ПО и итогах
Кроме списка полей, ВЫБОР используют в:
ГДЕ, когда фильтр зависит от параметра:ГДЕ ВЫБОР КОГДА &ВсеКонтрагенты ТОГДА ИСТИНА ИНАЧЕ Продажи.Контрагент = &Контрагент КОНЕЦ. Рабочий приём, но запрос с таким фильтром оптимизируется хуже. Если можно, соберите текст запроса в коде и добавьте условие только когда оно нужно.УПОРЯДОЧИТЬ ПО, когда нужен свой порядок: сначала «Просрочен», потом «Действует», потом прочие. Порядок задают числом:ВЫБОР КОГДА ... ТОГДА 1 КОГДА ... ТОГДА 2 ИНАЧЕ 3 КОНЕЦ.ИТОГИ— внутри агрегатных функций, как в примере сСУММАвыше.
Группировка по вычисленному признаку
Группировать по выражению ВЫБОР напрямую нельзя: в СГРУППИРОВАТЬ ПО ставят поля, а не вычисления. Рабочий путь — два шага через вложенный запрос или временную таблицу.
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
ВЫБОР
КОГДА Продажи.Сумма >= 100000 ТОГДА "Крупная"
ИНАЧЕ "Обычная"
КОНЕЦ КАК Категория
ПОМЕСТИТЬ ВТПродажи
ИЗ
РегистрНакопления.Продажи КАК Продажи
;
ВЫБРАТЬ
ВТПродажи.Категория КАК Категория,
КОЛИЧЕСТВО(*) КАК Количество
ИЗ
ВТПродажи КАК ВТПродажи
СГРУППИРОВАТЬ ПО
ВТПродажи.Категория
Первый запрос помечает каждую строку, второй считает. Заодно признак вычисляется один раз, а не в каждом месте, где нужен.
Сравнение со ссылками и перечислениями
Значения перечислений и предопределённые элементы в КОГДА сравнивают через ЗНАЧЕНИЕ():
ВЫБОР
КОГДА Документы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт) ТОГДА 1
ИНАЧЕ 0
КОНЕЦ
Литералов вроде "Закрыт" вместо ЗНАЧЕНИЕ не пишите: строка не равна значению перечисления.
Параметры внутри ВЫБОР
Параметры (&Параметр) можно подставлять и в условие, и в результат. Типичный пример — переключатель режима отчёта:
ВЫБОР
КОГДА &ПоКонтрагентам ТОГДА Продажи.Контрагент
ИНАЧЕ Продажи.Организация
КОНЕЦ КАК Разрез
Ветви возвращают разные типы ссылок, поэтому поле будет составного типа. Для вывода в отчёт это нормально, а для дальнейших соединений приведите типы или разделите запрос.
NULL, пустые значения и другие ловушки
Сравнение с NULL через = не даёт истины. Проверять нужно так:
ВЫБОР
КОГДА Остатки.Количество ЕСТЬ NULL ТОГДА 0
ИНАЧЕ Остатки.Количество
КОНЕЦ
Для этой же цели есть функция ЕСТЬNULL(Остатки.Количество, 0), она короче. Но ВЫБОР нужен, когда ветвей больше двух или значения разные для разных причин.
Самые частые проблемы:
| Симптом | Причина | Что сделать |
|---|---|---|
Вместо нуля приходит NULL | Нет ИНАЧЕ или данные из левого соединения | Добавить ИНАЧЕ 0 или обернуть в ЕСТЬNULL |
| Сумма не сходится с эталоном | Ветви пересекаются, сработала не та | Переставить условия от узкого к широкому |
| «Несовместимые типы» при выполнении | Ветви возвращают разные типы, поле используется в сравнении | Привести через ВЫРАЗИТЬ |
| Запрос не компилируется | Пропущен КОНЕЦ или ТОГДА | Проверить парность ключевых слов |
| Лишние строки в отчёте | ИЛИ и И без скобок | Расставить скобки явно |
| Ветвь по пустой ссылке не срабатывает | Сравнение идёт с NULL или со строкой | Сравнивать с ЗНАЧЕНИЕ(Справочник.Имя.ПустаяСсылка) |
Проверка запроса по шагам
Если результат вызывает сомнения, сделайте так:
- Откройте консоль запросов и вставьте запрос целиком.
- Временно выведите в результат сами признаки, на которых строится
КОГДА, рядом с итоговым значением. - Отдельно посмотрите строки с
NULLв этих признаках. - Только потом правьте порядок ветвей.
Частые вопросы
Можно ли в 1С написать ВЫБОР без ИНАЧЕ?
NULL. Если дальше поле суммируется или сравнивается, лучше задавать ИНАЧЕ явно.Чем ВЫБОР в запросе отличается от Если в коде?
Если в коде — оператор, он управляет ходом программы. ВЫБОР в запросе — выражение, оно вычисляется для каждой строки и возвращает значение. Писать вызовы процедур или присваивания внутри него нельзя.Сколько ветвей КОГДА можно указать?
Работает ли ВЫБОР внутри функции СУММА или КОЛИЧЕСТВО?
СУММА(ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ 0 КОНЕЦ). Это быстрее, чем несколько отдельных запросов по одной таблице.Почему ВЫБОР в соединении тормозит?
ПО мешает использовать индекс. Если таблицы большие, замените его двумя соединениями по простому равенству.