Разработка и Конфигурация · 11 мин
Case when в 1С: условная логика в запросах и коде
Как работает конструкция ВЫБОР КОГДА ТОГДА КОНЕЦ в запросах 1С и тернарный оператор в коде: синтаксис, примеры, типичные ошибки и альтернативы.
В языке запросов 1С нет привычной конструкции CASE WHEN в том виде, в каком её знают разработчики SQL, — вместо неё используется русскоязычный аналог ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ. Она выполняет ту же задачу: возвращает разные значения в зависимости от условий прямо внутри запроса, без дополнительной обработки результата в коде.
Отдельно существует условная логика во встроенном языке 1С: классический Если ... Тогда ... КонецЕсли; и тернарный оператор ?(), который часто ищут по запросу «case when в 1с», когда нужно записать короткое условие в одну строку. Понимание различий между этими тремя инструментами экономит время и избавляет от лишних циклов по выборке.
Ниже разобраны синтаксис обеих конструкций, рабочие примеры для типовых задач (скидки, статусы, группировки), ограничения языка запросов и ошибки, которые чаще всего приводят к неверным данным в отчётах.
Синтаксис ВЫБОР КОГДА в языке запросов 1С
Конструкция состоит из ключевых слов ВЫБОР, одной или нескольких пар КОГДА ... ТОГДА, необязательной ветки ИНАЧЕ и завершающего слова КОНЕЦ. Условия проверяются сверху вниз, и срабатывает первое истинное — остальные ветки не вычисляются. Это важно при пересекающихся диапазонах: более частные условия нужно ставить выше.
Пример расчёта категории суммы продажи:
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
Продажи.Сумма КАК Сумма,
ВЫБОР
КОГДА Продажи.Сумма >= 100000
ТОГДА "Крупная"
КОГДА Продажи.Сумма >= 10000
ТОГДА "Средняя"
ИНАЧЕ "Мелкая"
КОНЕЦ КАК КатегорияПродажи
ИЗ
РегистрНакопления.Продажи.Обороты КАК Продажи
Ветка ИНАЧЕ формально необязательна, но без неё все строки, не попавшие ни под одно условие, получат значение NULL. На практике это частая причина «пустых» колонок в отчётах, поэтому ветку ИНАЧЕ стоит писать всегда — даже если это просто ИНАЧЕ 0 или ИНАЧЕ "".
Типовые сценарии использования в запросах
Подмена значений и статусы документов
Самый частый сценарий — преобразование технических значений в понятные пользователю строки. Например, вывести состояние заказа словами вместо ссылки на перечисление или вычисляемого признака:
ВЫБОР
КОГДА Заказ.Проведен
ТОГДА "Проведён"
КОГДА Заказ.ПометкаУдаления
ТОГДА "Помечен на удаление"
ИНАЧЕ "Черновик"
КОНЕЦ КАК Статус
Условная агрегация
Вторая типовая задача — посчитать суммы по разным условиям в одном проходе, без объединения нескольких запросов. Для этого ВЫБОР вкладывается внутрь агрегатной функции СУММА:
ВЫБРАТЬ
СУММА(ВЫБОР КОГДА Обороты.ВидОперации = &Продажа ТОГДА Обороты.Сумма ИНАЧЕ 0 КОНЕЦ) КАК СуммаПродаж,
СУММА(ВЫБОР КОГДА Обороты.ВидОперации = &Возврат ТОГДА Обороты.Сумма ИНАЧЕ 0 КОНЕЦ) КАК СуммаВозвратов
ИЗ
РегистрНакопления.Взаиморасчеты.Обороты КАК Обороты
Такой приём заменяет сводную таблицу в коде и выполняется на стороне СУБД, что заметно быстрее на больших объёмах данных.
Обработка NULL и пустых ссылок
При левых соединениях поля присоединённой таблицы могут содержать NULL. ВЫБОР вместе с оператором ЕСТЬ NULL позволяет подставить значение по умолчанию:
ВЫБОР
КОГДА Цены.Цена ЕСТЬ NULL
ТОГДА 0
ИНАЧЕ Цены.Цена
КОНЕЦ КАК Цена
Для этой задачи есть и более короткая функция ЕСТЬNULL(Цены.Цена, 0) — она предпочтительнее, когда нужна именно подмена NULL без сложных условий.
- 1Откройте консоль запросов или конструктор запроса в конфигураторе
- 2Добавьте вычисляемое поле и запишите конструкцию ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ
- 3Задайте полю понятный псевдоним через КАК
- 4Выполните запрос на тестовых данных и проверьте строки, попавшие в ветку ИНАЧЕ
- 5Проверьте, что в результате нет неожиданных NULL в новой колонке
Тернарный оператор ?() во встроенном языке
Во встроенном языке 1С нет конструкции CASE, но есть тернарный оператор выбора: ?(Условие, ЗначениеЕслиИстина, ЗначениеЕслиЛожь). Он вычисляет условие и возвращает одно из двух значений, что удобно для коротких присваиваний:
СтавкаНДС = ?(Товар.ОблагаетсяНДС, 20, 0);
ТекстСтатуса = ?(Документ.Проведен, "Проведён", "Не проведён");
Важная особенность: оператор ?() вычисляет оба выражения до выбора результата. Если в одной из веток стоит вызов функции с обращением к базе или потенциальная ошибка (например, деление), она выполнится в любом случае. Поэтому в ветках тернарного оператора безопасно использовать только простые значения и выражения без побочных эффектов.
Если веток больше двух или логика сложная, читаемость тернарного оператора резко падает — вложенные ?(... ?(...)) считаются плохим стилем. Здесь правильнее использовать полноценный блок условий.
Блок Если...ИначеЕсли...КонецЕсли как полная замена CASE
Когда условий несколько и в каждой ветке нужно выполнить не одно выражение, а последовательность действий, используется классический условный оператор встроенного языка:
Если СуммаДокумента >= 100000 Тогда
Категория = "Крупная";
ТребуетсяСогласование = Истина;
ИначеЕсли СуммаДокумента >= 10000 Тогда
Категория = "Средняя";
Иначе
Категория = "Мелкая";
КонецЕсли;
Как и в запросе, условия проверяются сверху вниз, и выполняется только первая истинная ветка. При пересекающихся условиях порядок критичен: если поменять местами первые две проверки из примера выше, ветка «Крупная» никогда не сработает, потому что любая сумма больше 100000 одновременно больше и 10000.
Для сравнения одной переменной с набором констант (типичный случай switch/case из других языков) в 1С также применяют соответствие: ключ — вариант значения, значение — результат. Это избавляет от длинных цепочек ИначеЕсли и упрощает добавление новых вариантов.
Чем заменить CASE в разных контекстах
Подходит для вычисляемых полей, условной агрегации и подмены значений на уровне СУБД. Не работает вне запроса.
Короткие присваивания с двумя вариантами. Ограничение: обе ветки вычисляются, вложенность ухудшает читаемость.
Универсальный вариант для любой логики в коде, включая несколько операторов в ветке. Более многословен.
Выбор по точному совпадению значения из фиксированного списка. Не подходит для диапазонов и сложных условий.
Частые ошибки и как их проверить
Первая ошибка — отсутствие ветки ИНАЧЕ. Запрос выполняется без ошибок, но часть строк получает NULL, и дальнейшие расчёты (суммирование, сравнение) дают заниженные или пустые результаты. Признак проблемы: в отчёте колонка заполнена не во всех строках, хотя логика этого не предполагает.
Вторая ошибка — неправильный порядок условий при пересекающихся диапазонах. Проверка проста: возьмите граничные значения (ровно 10000, ровно 100000) и проследите, в какую ветку они попадают. Границы диапазонов должны быть зафиксированы в условиях явно — через >= или >, а не «по памяти».
Третья ошибка — сравнение с NULL обычным знаком равенства. Условие КОГДА Поле = NULL никогда не будет истинным; правильная форма — КОГДА Поле ЕСТЬ NULL. Эта ошибка не вызывает исключения, поэтому обнаруживается только по неверным данным.
Производительность и ограничения
Конструкция ВЫБОР выполняется на стороне СУБД и сама по себе не является узким местом. Проблемы начинаются, когда условие КОГДА содержит соединение с другой таблицей или вложенный запрос — такие конструкции могут выполняться для каждой строки результата. Если внутри условия нужны данные из другой таблицы, надёжнее заранее соединить таблицы в секции ИЗ, а в ВЫБОР использовать уже готовые поля.
Второе ограничение — количество веток. Формально язык не запрещает десятки условий КОГДА, но запрос с двадцатью ветками трудно читать и отлаживать. Если вариантов много и все они сводятся к сопоставлению «значение → результат», правильнее завести таблицу соответствий (например, регистр сведений или табличную часть настроек) и получить результат соединением.
При работе с СКД (системой компоновки данных) помните, что вычисляемые поля и ресурсы отчёта тоже поддерживают ВЫБОР, и часто логичнее вынести условие из текста запроса в выражение ресурса — тогда итоги по группировкам будут считаться корректно без доработки запроса.
Как проверить результат
Все строки имеют значение из одной из веток
Есть пустые значения или NULL — не хватает ветки ИНАЧЕ
Сумма ровно на границе попадает в задуманную категорию
Граничное значение попадает не в ту ветку — перепутаны > и >=
Сумма по веткам сходится с общей суммой без условий
Часть суммы потеряна — условие не покрывает все строки
Частые вопросы
Есть ли в языке запросов 1С точный аналог SQL CASE WHEN?
Да, это конструкция ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ. По логике она полностью соответствует searched CASE из SQL: условия проверяются последовательно, срабатывает первая истинная ветка, а ИНАЧЕ задаёт значение по умолчанию.
Можно ли использовать ВЫБОР в секции условий ГДЕ?
Да, конструкция допустима в ГДЕ, но чаще условие проще записать напрямую через И/ИЛИ. ВЫБОР в ГДЕ оправдан, когда критерий фильтрации сам зависит от данных строки, например сравнение с разными порогами для разных категорий.
Почему тернарный оператор ?() вызывает ошибку, хотя условие ложно?
Потому что обе ветки оператора вычисляются до выбора результата. Если в невыбранной ветке есть деление на ноль, обращение к свойству неопределённого значения или запрос к базе, ошибка возникнет всё равно. Вынесите такие выражения в обычный Если...КонецЕсли.
Что быстрее: ВЫБОР в запросе или обработка выборки в коде?
Почти всегда ВЫБОР в запросе: вычисление происходит на стороне СУБД, а в код передаётся уже готовый результат. Обработка в цикле оправдана только когда логика требует вызова функций встроенного языка, недоступных в запросе.
Как заменить switch по строковым значениям в коде 1С?
Используйте цепочку Если...ИначеЕсли либо соответствие, где ключ — строковое значение, а значение — результат или имя процедуры. Соответствие удобнее, когда вариантов много и список периодически расширяется.
Работает ли ВЫБОР в динамических списках и СКД?
Да. В СКД конструкция поддерживается в тексте запроса набора данных, в выражениях вычисляемых полей и ресурсов. В динамических списках произвольный запрос с ВЫБОР также допустим, но нужно следить, чтобы основная таблица и ключевые поля были указаны корректно.
Что важно запомнить
В запросах 1С роль CASE WHEN выполняет ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ — всегда пишите ветку ИНАЧЕ и следите за порядком условий. В коде для коротких условий используйте ?(), помня что обе ветки вычисляются, а для сложной логики — Если...ИначеЕсли...КонецЕсли.