ВЫБОР... КОГДА... ТОГДА в запросах 1С 8.3: как писать и не тормозить

· 10 мин

Содержание

КОГДА в запросе 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, а не языковые средства запроса.

Если выражение «не принимается» в вычисляемом поле, проверьте, не вставлен ли в него кусок запроса целиком.

Как проверить результат и найти узкое место

Пишите и проверяйте запрос не в боевой форме, а в консоли запросов. Подойдёт любая внешняя обработка «Консоль запросов»: вставьте текст, задайте параметры, посмотрите результат и время выполнения.

Порядок проверки:

  1. Выполните запрос на малом отборе (одна ссылка, один день) и убедитесь, что значения верные.
  2. Проверьте границы условий: значения ровно на пороге, пустые ссылки, NULL из соединений.
  3. Прогоните на полном объёме и сравните время с версией без ВЫБОР. Если разница заметна, ищите ВЫБОР в ГДЕ и в условиях соединения.
  4. Включите показ плана запроса в консоли (если версия обработки умеет) и посмотрите, по каким таблицам идёт полное сканирование.

Для боевой базы с «живыми» медленными запросами подходят замеры производительности и технологический журнал: по нему видно, какой именно запрос занимает время и сколько строк читает СУБД. Настраивать технологический журнал лучше вместе с администратором: он создаёт нагрузку на диск.

Проверка запроса с ВЫБОР перед выкладкой
Выполнено: 0 / 5

Частые вопросы

Можно ли в одном ВЫБОР использовать несколько КОГДА?
Да, количество веток не ограничено. Они проверяются сверху вниз, срабатывает первая с истинным условием.
Что вернёт ВЫБОР, если не сработало ни одно условие и нет ИНАЧЕ?
Значение NULL. Поэтому ветку ИНАЧЕ лучше писать всегда.
Можно ли вкладывать ВЫБОР в ВЫБОР?
Можно, в любую ветку ТОГДА или ИНАЧЕ. Но больше двух-трёх уровней читать тяжело: вынесите промежуточный результат во временную таблицу.
Есть ли `КОГДА` в обычном коде 1С, не в запросе?
Нет. На встроенном языке используется Если ... ИначеЕсли ... Иначе ... КонецЕсли.
Как использовать ВЫБОР внутри СУММА?
Передайте в агрегатную функцию выражение: СУММА(ВЫБОР КОГДА Условие ТОГДА Поле ИНАЧЕ 0 КОНЕЦ). Так считаются условные суммы за один проход по таблице.