ВЫБОР... КОГДА... ТОГДА в запросах 1С 8.3: как писать и не тормозить
Содержание
КОГДА в запросе 1С — это ветка конструкции ВЫБОР. Она проверяет условие и возвращает значение для строки результата: ВЫБОР КОГДА Сумма > 1000 ТОГДА "Крупная" ИНАЧЕ "Обычная" КОНЕЦ. Отдельно слово КОГДА не работает
, только внутри ВЫБОР ... КОНЕЦ.
Конструкция работает как CASE в SQL. Через неё в запросе делают разметку строк, условные суммы, подстановку значений вместо NULL и разбивку на группы. Ниже синтаксис, ловушки с типами, влияние на скорость и способы проверить, что запрос не тормозит.
Как записывается конструкция: базовые правила
Общий вид:
ВЫБОР
КОГДА <Условие1> ТОГДА <Результат1>
КОГДА <Условие2> ТОГДА <Результат2>
ИНАЧЕ <РезультатПоУмолчанию>
КОНЕЦ
Ветки проверяются сверху вниз. Срабатывает первая, у которой условие истинно, остальные не смотрятся. Поэтому порядок веток — это логика, а не оформление.
Пример: разметить остатки товаров на складе.
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоОстаток КАК Количество,
ВЫБОР
КОГДА Остатки.КоличествоОстаток <= 0 ТОГДА "Нет в наличии"
КОГДА Остатки.КоличествоОстаток < 10 ТОГДА "Заканчивается"
ИНАЧЕ "Достаточно"
КОНЕЦ КАК Статус
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
Если остаток равен 5, первое условие ложно, второе истинно. Результат — «Заканчивается». Поменяйте ветки местами, и все остатки до 10 попадут в «Заканчивается», даже нулевые.
Что можно ставить в условие
- 📌 сравнения:
=,<>,>,<,>=,<=; - 📌 логика:
И,ИЛИ,НЕ, со скобками для приоритета; - 📌 проверки:
ЕСТЬ NULL,ССЫЛКА,В (...),МЕЖДУ ... И ...,ПОДОБНО; - 📌 выражения с функциями:
ГОД(Дата) = 2025,ПОДСТРОКА(Код, 1, 2) = "00".
Результат тоже выражение: число, строка, дата, ссылка, булево, поле другой таблицы, вложенный ВЫБОР.
Что будет без ИНАЧЕ
Ветка ИНАЧЕ необязательна. Если ни одно КОГДА не сработало и ИНАЧЕ нет, вернётся NULL, а не пустая строка и не ноль.
NULL в сумме или сравнении даёт неожиданные результаты: NULL > 0 не истинно и не ложно. Всегда пишите ИНАЧЕ, даже если уверены, что условия покрывают все случаи. Данные меняются, условия нет.
Один оператор или два: КОГДА и ВЫБОР на практике
Новички часто думают, что КОГДА и ВЫБОР — разные операторы. Это одна конструкция: ВЫБОР открывает, КОГДА задаёт условие, ТОГДА — результат, КОНЕЦ закрывает.
Две путаницы встречаются постоянно.
Запрос и встроенный язык. В коде на встроенном языке 1С нет ВЫБОР ... КОГДА: там Если ... Тогда ... ИначеЕсли ... Иначе ... КонецЕсли. ВЫБОР существует только в тексте запроса и в выражениях системы компоновки данных (СКД).
Простая и расширенная форма. В SQL бывает CASE поле WHEN значение THEN .... В запросах 1С условие пишется полностью в каждой ветке: КОГДА Поле = Значение ТОГДА. Сокращённого варианта нет.
| Задача | Чем решать | Почему |
|---|---|---|
| Разные значения по диапазонам | ВЫБОР КОГДА | Каждая ветка — своё условие |
Заменить NULL на значение | ЕСТЬNULL(Поле, 0) | Короче и быстрее читается |
| Привести тип | ВЫРАЗИТЬ(... КАК ...) | ВЫБОР тип не меняет |
| Отбор строк | ГДЕ | ВЫБОР строки не отбрасывает |
| Условная сумма | СУММА(ВЫБОР ...) | Один проход вместо нескольких запросов |
Типичные ошибки в запросах с КОГДА
Составной тип результата
Если ветки возвращают разные типы, поле получает составной тип. Например, ТОГДА 0 в одной ветке и ТОГДА Остатки.Номенклатура в другой. С таким полем неудобно работать: нельзя сгруппировать по нему без потерь, плохо ложится на индексы временных таблиц, при выводе в СКД ломается формат.
Решение — привести результат явно:
ВЫБОР
КОГДА Продажи.Сумма > 0 ТОГДА ВЫРАЗИТЬ(Продажи.Сумма КАК ЧИСЛО(15, 2))
ИНАЧЕ 0
КОНЕЦ
Длина строки
Строки в ветках разной длины сводятся к максимальной, но если одна из ветвей возвращает строку неограниченной длины (например, реквизит типа «Строка» без длины), весь результат становится неограниченным. Такие поля нельзя использовать в СГРУППИРОВАТЬ ПО, УПОРЯДОЧИТЬ ПО и в условиях сравнения. Оборачивайте в ВЫРАЗИТЬ(... КАК СТРОКА(50)).
Условие не покрывает NULL
После левого соединения поля правой таблицы могут быть NULL. Условие КОГДА Цены.Цена > 0 ТОГДА ... для NULL не сработает, и строка уйдёт в ИНАЧЕ. Если нужна иная логика для отсутствующей цены, пишите отдельную ветку: КОГДА Цены.Цена ЕСТЬ NULL ТОГДА "Цена не задана". И ставьте её раньше остальных.
Порядок веток
Пересекающиеся условия дают результат по первой подошедшей ветке. Диапазоны проверяйте от узкого к широкому и перепроверяйте на границах: 0, 10, 100.
Как не замедлить запрос с КОГДА
Сам по себе ВЫБОР в списке полей выборки обычно дёшев: СУБД считает выражение для каждой уже выбранной строки. Проблемы начинаются, когда он попадает в места, где работают индексы.
ВЫБОР в условии отбора
Условие вида ГДЕ ВЫБОР КОГДА ... ТОГДА ... КОНЕЦ = &Значение не позволяет СУБД использовать индекс по полю, потому что поле завернуто в выражение. Запрос идёт по всей таблице.
Перепишите так, чтобы поле стояло само по себе:
// Медленно
ГДЕ
ВЫБОР КОГДА &ТолькоОплаченные ТОГДА Док.Оплачен ИНАЧЕ ИСТИНА КОНЕЦ = ИСТИНА
// Лучше
ГДЕ
(НЕ &ТолькоОплаченные ИЛИ Док.Оплачен = ИСТИНА)
Если условий много, соберите текст запроса из частей: добавляйте нужные условия в ГДЕ только когда они действительно нужны.
ВЫБОР в условии соединения
Соединение по ВЫБОР КОГДА ... ТОГДА Поле1 ИНАЧЕ Поле2 КОНЕЦ = Т.Поле тоже не использует индексы и на больших таблицах превращается в перебор. Разбейте на два запроса с ОБЪЕДИНИТЬ ВСЕ, в каждом своё условие соединения. Результат одинаковый, план запроса другой.
Условные суммы в одном проходе
Самый полезный приём: вместо нескольких запросов по разным условиям посчитать всё за один проход.
ВЫБРАТЬ
Продажи.Менеджер КАК Менеджер,
СУММА(ВЫБОР КОГДА Продажи.Оплачен ТОГДА Продажи.Сумма ИНАЧЕ 0 КОНЕЦ) КАК Оплачено,
СУММА(ВЫБОР КОГДА НЕ Продажи.Оплачен ТОГДА Продажи.Сумма ИНАЧЕ 0 КОНЕЦ) КАК Долг
ИЗ
Документ.РеализацияТоваровУслуг КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Менеджер
Таблица читается один раз. Два отдельных запроса прочитали бы её дважды.
Вложенность и повторы
Глубокий вложенный ВЫБОР на пять уровней читается плохо и растёт в объёме при каждом изменении. Одно и то же выражение, повторённое в пяти местах запроса, лучше вычислить один раз во временной таблице и дальше брать готовое поле.
Примеры реальных задач
Разбивка клиентов по размеру оборота
ВЫБОР
КОГДА Обороты.СуммаОборот >= 1000000 ТОГДА "A"
КОГДА Обороты.СуммаОборот >= 200000 ТОГДА "B"
ИНАЧЕ "C"
КОНЕЦ КАК Группа
Порядок важен: сначала проверяем самую высокую границу. Клиент с оборотом 1 500 000 сразу получает «A», а не «B».
Знак операции в регистре
В регистре накопления вид движения — приход или расход. Чтобы получить сальдо одним полем:
СУММА(ВЫБОР КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Количество
ИНАЧЕ -Движения.Количество КОНЕЦ) КАК Сальдо
Метка периода
ВЫБОР
КОГДА Док.Дата < &НачалоПериода ТОГДА "До периода"
КОГДА Док.Дата <= &КонецПериода ТОГДА "В периоде"
ИНАЧЕ "После периода"
КОНЕЦ КАК Период
Подстановка значения по умолчанию
ВЫБОР
КОГДА Контрагенты.ИНН = "" ТОГДА "ИНН не заполнен"
ИНАЧЕ Контрагенты.ИНН
КОНЕЦ КАК ИНН
Если у реквизита ИНН длина ограничена, составного типа не возникнет: результат получит наибольшую длину из двух веток.
Свой порядок сортировки
Статусы по алфавиту идут не в том порядке, в котором нужны людям. Добавьте в запрос служебное числовое поле и упорядочьте по нему:
ВЫБРАТЬ
Заказы.Ссылка КАК Заказ,
Заказы.Статус КАК Статус,
ВЫБОР
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.ВРаботе) ТОГДА 1
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Новый) ТОГДА 2
ИНАЧЕ 3
КОНЕЦ КАК Порядок
ИЗ
Документ.ЗаказПокупателя КАК Заказы
УПОРЯДОЧИТЬ ПО
Порядок
Названия значений перечисления здесь условные: подставьте свои. Служебное поле Порядок можно не выводить в отчёт, оно нужно только для сортировки.
Счётчики по условиям
Сколько заказов просрочено, а сколько в срок, в одном запросе:
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВЫБОР КОГДА Заказы.СрокОтгрузки < &Сегодня ТОГДА Заказы.Ссылка КОНЕЦ) КАК Просрочено,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВЫБОР КОГДА Заказы.СрокОтгрузки >= &Сегодня ТОГДА Заказы.Ссылка КОНЕЦ) КАК ВСрок
Здесь ИНАЧЕ намеренно нет. Для строк без совпадения возвращается NULL, а КОЛИЧЕСТВО значения NULL не считает. Это единственный случай, когда отсутствие ИНАЧЕ оправдано: именно ради пропуска строк.
Проверка на пустое значение
Пустая ссылка и NULL — разные вещи. Пустая ссылка существует и сравнивается: КОГДА Док.Контрагент = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка) ТОГДА "Не указан". NULL бывает только там, где записи в таблице нет вовсе, например в правой части левого соединения. Если в отчёте выходит пусто, а вы проверяете не то, строка уйдёт не в ту ветку.
Когда ВЫБОР лучше заменить
ВЫБОР — не единственный способ получить условное значение, и не всегда лучший.
- 🔹
ЕСТЬNULL(Поле, Значение)— если нужно только заменитьNULL. Записывается короче, чемВЫБОР КОГДА Поле ЕСТЬ NULL ТОГДА ... ИНАЧЕ Поле КОНЕЦ. - 🔹 Таблица соответствия. Если веток больше пяти-семи и они статичны (код → название группы), положите соответствие во временную таблицу и соедините. Правила хранятся данными, а не текстом запроса.
- 🔹 Условие в
ГДЕ. Если нужно просто исключить строки,ВЫБОРне нужен. - 🔹 Объединение запросов. Когда ветки обращаются к разным таблицам,
ОБЪЕДИНИТЬ ВСЕчитается проще и работает быстрее, чем сложныйВЫБОРнад соединением всего подряд. - 🔹 Обработка в коде. Если логика зависит от десятка параметров и меняется, возможно, проще вернуть сырые данные и разобрать их в коде. Но сначала проверьте, что объём данных это позволяет: гонять миллионы строк через цикл хуже, чем отдать расчёт СУБД.
Правило выбора простое. Если условие работает на уровне строки результата, берите ВЫБОР. Если оно решает, какие строки вообще нужны, — ГДЕ или соединение.
Где выполняется запрос: клиенты и СКД
Запрос с ВЫБОР всегда выполняется на стороне сервера. Тонкий клиент, веб-клиент и толстый клиент разницы не дают: клиентское приложение вызывает серверную процедуру, она собирает результат. В управляемых формах запрос создают в процедуре с директивой &НаСервере, на клиенте объект Запрос не создаётся.
Разница есть в другом: в схеме компоновки данных (СКД) ВЫБОР встречается дважды.
- 🔸 В тексте запроса набора данных синтаксис тот же, что описан выше.
- 🔸 В выражениях вычисляемых полей и ресурсов компоновки применяется свой язык выражений. Синтаксис конструкции похож (
Выбор Когда ... Тогда ... Иначе ... Конец), но функции там другие, напримерЕстьNull, а не языковые средства запроса.
Если выражение «не принимается» в вычисляемом поле, проверьте, не вставлен ли в него кусок запроса целиком.
Как проверить результат и найти узкое место
Пишите и проверяйте запрос не в боевой форме, а в консоли запросов. Подойдёт любая внешняя обработка «Консоль запросов»: вставьте текст, задайте параметры, посмотрите результат и время выполнения.
Порядок проверки:
- Выполните запрос на малом отборе (одна ссылка, один день) и убедитесь, что значения верные.
- Проверьте границы условий: значения ровно на пороге, пустые ссылки,
NULLиз соединений. - Прогоните на полном объёме и сравните время с версией без
ВЫБОР. Если разница заметна, ищитеВЫБОРвГДЕи в условиях соединения. - Включите показ плана запроса в консоли (если версия обработки умеет) и посмотрите, по каким таблицам идёт полное сканирование.
Для боевой базы с «живыми» медленными запросами подходят замеры производительности и технологический журнал: по нему видно, какой именно запрос занимает время и сколько строк читает СУБД. Настраивать технологический журнал лучше вместе с администратором: он создаёт нагрузку на диск.
Частые вопросы
Можно ли в одном ВЫБОР использовать несколько КОГДА?
Что вернёт ВЫБОР, если не сработало ни одно условие и нет ИНАЧЕ?
NULL. Поэтому ветку ИНАЧЕ лучше писать всегда.Можно ли вкладывать ВЫБОР в ВЫБОР?
ТОГДА или ИНАЧЕ. Но больше двух-трёх уровней читать тяжело: вынесите промежуточный результат во временную таблицу.Есть ли `КОГДА` в обычном коде 1С, не в запросе?
Если ... ИначеЕсли ... Иначе ... КонецЕсли.Как использовать ВЫБОР внутри СУММА?
СУММА(ВЫБОР КОГДА Условие ТОГДА Поле ИНАЧЕ 0 КОНЕЦ). Так считаются условные суммы за один проход по таблице.