Условия в запросе 1С через ВЫБОР КОГДА ТОГДА: от простой ветки до каскада
Содержание
Условие в запросе 1С пишется конструкцией ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Это аналог CASE WHEN ... THEN ... ELSE ... END из SQL: платформа проверяет ветки сверху вниз и возвращает значение первой, где условие истинно. Нет подходящей ветки и нет ИНАЧЕ — результат будет NULL.
Функцию КОГДА в языке запросов 1С используют, чтобы раскрасить поле статусом, посчитать сумму только по части строк, подменить пустое значение или отсортировать список в своём порядке. Работает одинаково в БП, ЗУП, УТ и любой другой конфигурации на платформе 8.3: это часть языка запросов, а не прикладного решения.
Синтаксис ВЫБОР КОГДА и где его можно писать
Полная форма конструкции:
ВЫБОР
КОГДА <Условие1> ТОГДА <Выражение1>
КОГДА <Условие2> ТОГДА <Выражение2>
...
ИНАЧЕ <ВыражениеПоУмолчанию>
КОНЕЦ
Что здесь обязательно:
- 🔹 слово
ВЫБОРв начале иКОНЕЦв конце; - 🔹 хотя бы одна пара
КОГДА ... ТОГДА; - 🔸
ИНАЧЕ— необязательно, но без него непойманные строки получатNULL.
Английский вариант тоже работает: CASE WHEN ... THEN ... ELSE ... END. Смешивать русские и английские ключевые слова в одном запросе платформа позволяет, но читать такое тяжело.
Конструкция — это выражение, а не оператор. Значит, её можно поставить везде, где запрос ждёт выражение:
| Место в запросе | Пример задачи |
|---|---|
ВЫБРАТЬ | вычисляемое поле «Статус», «Знак суммы» |
ГДЕ | условие, которое меняется от вида строки |
СГРУППИРОВАТЬ ПО | группировка по вычисленной категории |
УПОРЯДОЧИТЬ ПО | свой порядок сортировки |
внутри СУММА, КОЛИЧЕСТВО, МАКСИМУМ | условная агрегация |
условие соединения ПО | связь по разным полям в зависимости от типа |
Минимальный пример: подпишем вид движения регистра накопления понятным словом.
ВЫБРАТЬ
Движения.Номенклатура,
Движения.Количество,
ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА "Приход"
ИНАЧЕ "Расход"
КОНЕЦ КАК Направление
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
Псевдоним через КАК после КОНЕЦ ставьте всегда. Без него поле получит служебное имя, и обращаться к нему из кода или из схемы компоновки будет неудобно.
Что ставить после КОГДА: сравнение со значением и логическое выражение
После КОГДА всегда стоит логическое выражение. В SQL есть короткая форма CASE Поле WHEN 1 THEN ..., где сравнение подразумевается. В языке запросов 1С пишите сравнение явно в каждой ветке: КОГДА Поле = 1 ТОГДА.
На практике условия делятся на два вида.
Сравнение с конкретным значением
Поле сравнивается с литералом, параметром или предопределённым значением:
- 🔢 число или строка:
КОГДА Товары.СтавкаНДС = 20,КОГДА Клиенты.Код = "000123"; - 📌 параметр:
КОГДА Док.Организация = &Организация; - 📚 перечисление или предопределённый элемент:
КОГДА Док.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт); - ⬜ пустая ссылка:
КОГДА Док.Контрагент = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).
Функция ЗНАЧЕНИЕ() принимает только предопределённые значения, перечисления, пустые ссылки и виды движений. Обычный элемент справочника через неё не передать — для него нужен параметр.
Произвольное логическое выражение
Здесь допустимо всё, что пишут в ГДЕ:
- ⚖️ диапазоны:
КОГДА Остатки.Количество МЕЖДУ 1 И 10; - 📋 списки:
КОГДА Товары.Склад В (&ОсновныеСклады); - 🧩 комбинации:
КОГДА Док.Сумма > 100000 И НЕ Док.Проведен; - 🕳 проверка на NULL:
КОГДА Остатки.Количество ЕСТЬ NULL; - 🏷 проверка типа:
КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг; - 🔤 шаблон строки:
КОГДА Номенклатура.Наименование ПОДОБНО "%кабель%".
Пример классификации остатков по уровню:
ВЫБОР
КОГДА Остатки.КоличествоОстаток <= 0 ТОГДА "Нет в наличии"
КОГДА Остатки.КоличествоОстаток < 10 ТОГДА "Заканчивается"
ИНАЧЕ "В наличии"
КОНЕЦ КАК УровеньЗапаса
Порядок веток здесь решает. Остаток −2 попадает в первую ветку и дальше не проверяется. Если поменять первые две ветки местами, отрицательный остаток станет «Заканчивается».
Вложенные ВЫБОР и каскад условий
Внутри ТОГДА и ИНАЧЕ может стоять ещё один ВЫБОР. Глубину вложенности платформа не ограничивает на практике, но уже третий уровень читается плохо.
Пример: скидка зависит от вида клиента, а внутри вида — от суммы заказа.
ВЫБОР
КОГДА Заказ.Контрагент.ВидКлиента = ЗНАЧЕНИЕ(Перечисление.ВидыКлиентов.Оптовый)
ТОГДА ВЫБОР
КОГДА Заказ.СуммаДокумента >= 500000 ТОГДА 10
КОГДА Заказ.СуммаДокумента >= 100000 ТОГДА 7
ИНАЧЕ 5
КОНЕЦ
ИНАЧЕ ВЫБОР
КОГДА Заказ.СуммаДокумента >= 50000 ТОГДА 3
ИНАЧЕ 0
КОНЕЦ
КОНЕЦ КАК ПроцентСкидки
Перечисление ВидыКлиентов здесь условное — подставьте объект своей конфигурации.
Ту же логику можно развернуть в плоский каскад, объединив условия через И:
ВЫБОР
КОГДА Опт И Сумма >= 500000 ТОГДА 10
КОГДА Опт И Сумма >= 100000 ТОГДА 7
КОГДА Опт ТОГДА 5
КОГДА Сумма >= 50000 ТОГДА 3
ИНАЧЕ 0
КОНЕЦ
Как выбрать между формами:
| Форма | Когда удобна | Минус |
|---|---|---|
| Плоский каскад | до 5–7 веток, условия разные | общее условие повторяется в каждой ветке |
Вложенный ВЫБОР | есть явная иерархия «сначала вид, потом сумма» | много уровней отступов, легко потерять КОНЕЦ |
| Временная таблица с промежуточным полем | логика сложная, результат нужен в нескольких местах | лишний пакет в запросе |
Каждый вложенный ВЫБОР закрывается своим КОНЕЦ. Ставьте КОНЕЦ строго под соответствующим ВЫБОР — так непарное слово видно сразу.
Типы результата, NULL и приведение через ВЫРАЗИТЬ
Ветки могут возвращать значения разных типов. Платформа не запрещает этого: поле получит составной тип. Пример ВЫБОР КОГДА ... ТОГДА 1 ИНАЧЕ "нет" КОНЕЦ даст поле «Число или Строка». Дальше такое поле плохо суммируется, сравнивается и группируется — лучше держать ветки в одном типе.
NULL в условии и в результате
Три правила, которые ловят чаще всего:
- ❌
КОГДА Поле = NULLникогда не срабатывает. Сравнение с NULL даёт «неизвестно», и ветка пропускается. ПишитеКОГДА Поле ЕСТЬ NULL. - ❌ Если поле пришло из левого соединения и строки справа нет, любое сравнение этого поля — тоже «неизвестно». Строка уйдёт в
ИНАЧЕ, а не в ветку с условиемПоле <> 0. - ❌ Без
ИНАЧЕнепойманные строки даютNULL. ВСУММАони просто не учтутся, но в арифметикеNULL + 100тоже дастNULL.
Для простой подмены пустого значения короче функция ЕСТЬNULL:
ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
Это то же самое, что ВЫБОР КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL ТОГДА 0 ИНАЧЕ Остатки.КоличествоОстаток КОНЕЦ, только читается в одну строку. ВЫБОР нужен, когда условие сложнее, чем «значение отсутствует».
Строки и составные ссылки
Строковые реквизиты неограниченной длины (комментарии, многострочные описания) нельзя сравнивать в запросе напрямую. Сначала приведите их к строке фиксированной длины:
ВЫБОР
КОГДА ВЫРАЗИТЬ(Док.Комментарий КАК СТРОКА(200)) = "" ТОГДА "Без комментария"
ИНАЧЕ "Есть комментарий"
КОНЕЦ
Поле составного типа (например, Регистратор) разбирают оператором ССЫЛКА, а нужный реквизит достают через ВЫРАЗИТЬ:
ВЫБОР
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент
КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Контрагент
ИНАЧЕ ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
КОНЕЦ КАК Контрагент
ИНАЧЕ с пустой ссылкой здесь удерживает тип поля: «Контрагенты», а не «Контрагенты или NULL».
Как ВЫБОР влияет на скорость запроса
Сама конструкция вычисляется быстро — это обычное выражение на стороне СУБД. Тормозит не ВЫБОР, а то, что внутри него и где он стоит.
ВЫБОР в ГДЕ и в соединениях
Условие вида ГДЕ ВЫБОР КОГДА ... КОНЕЦ = 1 СУБД не может разложить на поиск по индексу. На большой таблице это означает полный просмотр. Порядок действий:
- Попробуйте переписать условие через
ИЛИиИбезВЫБОР:ГДЕ (Вид = 1 И Сумма > 0) ИЛИ (Вид = 2 И Сумма < 0). - Если условие касается виртуальной таблицы (
Остатки,Обороты,СрезПоследних), перенесите отбор в её параметры. Отбор внутри параметров выполняется до расчёта таблицы. - Если без
ВЫБОРне обойтись, сначала отберите строки простым условием по индексированному полю во временную таблицу, а вычисление сделайте уже в ней.
Неявные соединения через точку
Каждое Док.Контрагент.ВидКлиента внутри КОГДА — это соединение с таблицей справочника. Для поля составного типа Регистратор.Дата платформа соединяет таблицы всех типов, из которых состоит поле. Десять видов документов-регистраторов — десять соединений.
💡 Что помогает:
- 🔧 перед обращением к реквизиту составного поля приведите тип через
ВЫРАЗИТЬ— соединение останется одно; - 🔧 повторяющееся обращение через точку вынесите во временную таблицу один раз;
- 🔧 проверку типа делайте оператором
ССЫЛКАили функциейТИПЗНАЧЕНИЯ, а не сравнением реквизитов.
Повтор одного выражения
Если тот же ВЫБОР нужен в ВЫБРАТЬ, СГРУППИРОВАТЬ ПО и УПОРЯДОЧИТЬ ПО, его придётся написать трижды, и в группировке — точно так же, как в поле. Чище посчитать поле один раз во временной таблице:
ВЫБРАТЬ
Продажи.Номенклатура,
Продажи.СуммаОборот,
ВЫБОР
КОГДА Продажи.СуммаОборот >= 1000000 ТОГДА "A"
КОГДА Продажи.СуммаОборот >= 200000 ТОГДА "B"
ИНАЧЕ "C"
КОНЕЦ КАК Группа
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ.Группа,
КОЛИЧЕСТВО(ВТ.Номенклатура) КАК Позиций,
СУММА(ВТ.СуммаОборот) КАК Выручка
ИЗ
ВТ_Продажи КАК ВТ
СГРУППИРОВАТЬ ПО
ВТ.Группа
УПОРЯДОЧИТЬ ПО
ВТ.Группа
Имена ресурсов регистра Продажи в разных конфигурациях отличаются — сверьте их в конструкторе запроса.
Шаблоны кода с КОГДА для типовых задач
Условная сумма: приход и расход в одной строке
ВЫБРАТЬ
Движения.Номенклатура,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Пришло,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Ушло
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
Движения.Период МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
ИНАЧЕ 0 здесь обязателен по смыслу: если по товару не было ни одного прихода, без него получится NULL вместо нуля.
Подсчёт строк по условию
КОЛИЧЕСТВО не считает NULL, поэтому ветку «не подходит» просто оставляют без ИНАЧЕ:
КОЛИЧЕСТВО(ВЫБОР КОГДА Заказы.Проведен ТОГДА Заказы.Ссылка КОНЕЦ) КАК Проведено,
КОЛИЧЕСТВО(ВЫБОР КОГДА НЕ Заказы.Проведен ТОГДА Заказы.Ссылка КОНЕЦ) КАК Черновиков
Защита от деления на ноль
ВЫБОР
КОГДА Товары.Количество = 0 ТОГДА 0
ИНАЧЕ Товары.Сумма / Товары.Количество
КОНЕЦ КАК Цена
Свой порядок сортировки
Статусы хочется видеть не по алфавиту, а по ходу работы:
УПОРЯДОЧИТЬ ПО
ВЫБОР
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.КСогласованию) ТОГДА 1
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.ВРаботе) ТОГДА 2
ИНАЧЕ 3
КОНЕЦ,
Заказы.Дата
Значения перечисления СтатусыЗаказов в примере условные — у каждой конфигурации свой набор.
Логический флаг
ВЫБОР
КОГДА Остатки.КоличествоОстаток < Номенклатура.МинимальныйЗапас ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК НуженЗаказ
Реквизит МинимальныйЗапас взят для примера. Такой флаг удобно использовать в условном оформлении отчёта на СКД: выделять строки, где поле равно Истина.
Запросы с ВЫБОР сначала проверяйте в консоли запросов на копии базы или с ограничением ВЫБРАТЬ ПЕРВЫЕ 100. Ошибка в условии на рабочем регистре с миллионами строк повесит сеанс надолго.
Ошибки с ВЫБОР КОГДА и как их исправить
Ещё две ошибки не дают сообщений, но портят данные:
- 🧮 Разные типы в ветках.
ТОГДА 1 ИНАЧЕ "—"превращает поле в составное, и в отчёте суммирование по нему перестаёт работать. Держите ветки в одном типе, а прочерк рисуйте оформлением. - 🔗 Обращение через точку к полю из левого соединения. Если справа строки нет, условие даёт «неизвестно» и строка молча уходит в
ИНАЧЕ. ПроверяйтеЕСТЬ NULLпервой веткой.