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