Работа с условной логикой в запросах 1С:Предприятие 8.3 часто становится камнем преткновения для разработчиков. Оператор WHEN — один из самых мощных инструментов языка запросов, позволяющий реализовывать сложные условия прямо в SQL-подобных конструкциях без необходимости постобработки на встроенном языке. Однако его синтаксис и особенности применения вызывает вопросы даже у опытных программистов.
В этой статье мы разберём не только базовый синтаксис WHEN...THEN...ELSE, но и нюансы использования в связке с CASE, агрегатными функциями, подзапросами. Особое внимание уделим типичным ошибкам, которые приводят к некорректным результатам или падению производительности. Вы узнаете, как оптимизировать запросы с множественными условиями, избегать "ловушек" при работе с NULL-значениями в условной логике и применять WHEN для динамического формирования выходных данных.
Материал будет полезен как начинающим разработчикам, которые только осваивают язык запросов, так и опытным специалистам, стремящимся писать более эффективный и читаемый код. Все примеры приведены для актуальной версии платформы 1С:Предприятие 8.3.23 и выше, с учётом последних изменений в обработке условных выражений.
Базовый синтаксис оператора WHEN в 1С 8.3
Оператор WHEN в языке запросов 1С используется для реализации условной логики прямо в тексте запроса. Он всегда работает в паре с THEN и необязательным ELSE, формируя конструкцию, аналогичную оператору Если...Тогда...Иначе во встроенном языке.
Общий вид конструкции:
ВЫБРАТЬ
ВЫРАЖЕНИЕ1 КАК Поле1,
ВЫБОР
КОГДА Условие1 ТОГДА Значение1
КОГДА Условие2 ТОГДА Значение2
...
ИНАЧЕ ЗначениеПоУмолчанию
КОНЕЦ КАК Поле2
ИЗ
Таблица
Ключевые особенности синтаксиса:
- 🔹 Каждое условие
КОГДАдолжно заканчиватьсяТОГДАс указанием возвращаемого значения - 🔹 Блок
ИНАЧЕобязателен, если нужно обработать все возможные случаи (в противном случае для несоответствующих условий вернётся NULL) - 🔹 В одном выражении
ВЫБОРможно использовать неограниченное количествоКОГДАблоков - 🔹 Условия проверяются последовательно, и выполняется только первое истинное
Простейший пример с категоризацией клиентов по сумме заказов:
ВЫБРАТЬ
Клиенты.Наименование КАК Клиент,
ВЫБОР
КОГДА СУММА(Заказы.СуммаДокумента) > 1000000 ТОГДА "VIP"
КОГДА СУММА(Заказы.СуммаДокумента) > 500000 ТОГДА "Премиум"
КОГДА СУММА(Заказы.СуммаДокумента) > 100000 ТОГДА "Стандарт"
ИНАЧЕ "Базовый"
КОНЕЦ КАК Категория
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Клиент
СГРУППИРОВАТЬ ПО
Клиенты.Наименование
WHEN vs CASE: в чём разница и когда что применять
Многие разработчики путают конструкции WHEN и CASE, хотя на самом деле WHEN является частью CASE. В языке запросов 1С существует два варианта записи условных выражений:
1. Простое условное выражение (без ключевого слова CASE):
ВЫБРАТЬ
ВЫРАЖЕНИЕ КАК Поле,
ВЫБОР
КОГДА Условие1 ТОГДА Значение1
КОГДА Условие2 ТОГДА Значение2
КОНЕЦ КАК Результат
2. Полная форма с CASE (аналогична стандартному SQL):
ВЫБРАТЬ
ВЫРАЖЕНИЕ КАК Поле,
CASE
WHEN Условие1 THEN Значение1
WHEN Условие2 THEN Значение2
ELSE ЗначениеПоУмолчанию
END КАК Результат
Разница между ними чисто синтаксическая — функциональность идентична. Однако есть нюансы:
| Критерий | WHEN (без CASE) | CASE WHEN |
|---|---|---|
| Совместимость | Только 1С | Стандарт SQL, работает и в других СУБД |
| Читаемость | Привычнее для 1С-разработчиков | Универсальнее для специалистов, знающих SQL |
| Поддержка в старых версиях | Доступно с 8.2 | Требует 8.3.6+ для полной поддержки |
| Использование в подзапросах | Без ограничений | Может вызывать ошибки в сложных конструкциях |
Практический совет: если вы пишете запрос, который потом может использоваться за пределами 1С (например, в внешних отчётах с прямым SQL), лучше использовать CASE WHEN. Для внутренних нужд системы удобнее привычный синтаксис с ВЫБОР...КОГДА.
При отладке сложных условий с WHEN используйте конструкцию ВЫРАЗИТЬ(Условие КАК СТРОКА) в отдельном поле запроса — это поможет увидеть, какие именно условия срабатывают для каждой строки.
Практические примеры использования WHEN в реальных задачах
Рассмотрим типичные сценарии, где оператор WHEN становится незаменимым инструментом.
1. Динамическое формирование наименований
Частая задача — создание "умных" наименований, которые изменяются в зависимости от статуса или свойств объекта:
ВЫБРАТЬ
Документы.Номер КАК Номер,
Документы.Дата КАК Дата,
ВЫБОР
КОГДА Документы.ПометкаУдаления = ИСТИНА ТОГДА "[УДАЛЕН] " + Документы.Наименование
КОГДА Документы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДокументов.Одобрен) ТОГДА "✓ " + Документы.Наименование
КОГДА Документы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыДокументов.НаУтверждении) ТОГДА "? " + Документы.Наименование
ИНАЧЕ Документы.Наименование
КОНЕЦ КАК ПолноеНаименование
ИЗ
Документ.РеализацияТоваровУслуг КАК Документы
2. Расчёт бонусов по сложным правилам
WHEN идеально подходит для реализации многоуровневых систем скидок или бонусов:
ВЫБРАТЬ
Клиенты.Наименование КАК Клиент,
СУММА(Заказы.СуммаДокумента) КАК ОбщаяСумма,
ВЫБОР
КОГДА СУММА(Заказы.СуммаДокумента) > 1000000 ТОГДА СУММА(Заказы.СуммаДокумента) * 0.1
КОГДА СУММА(Заказы.СуммаДокумента) > 500000 ТОГДА СУММА(Заказы.СуммаДокумента) * 0.05
КОГДА Клиенты.ДатаРегистрации < ДОБАВИТЬКДАТЕ(ТЕКУЩАЯДАТА(), ГОД, -1) ТОГДА 5000
ИНАЧЕ 0
КОНЕЦ КАК БонусныеБаллы
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Клиент
СГРУППИРОВАТЬ ПО
Клиенты.Наименование,
Клиенты.ДатаРегистрации
3. Фильтрация данных по сложным критериям
WHEN можно использовать прямо в секции WHERE для создания гибких фильтров:
ВЫБРАТЬ
Номенклатура.Наименование КАК Товар,
Остатки.КоличествоОстаток КАК Остаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки КАК Остатки
ПО Номенклатура.Ссылка = Остатки.Номенклатура
ГДЕ
ВЫБОР
КОГДА &ПоказыватьТолькоДефицит ТОГДА Остатки.КоличествоОстаток < 0
КОГДА &ПоказыватьТолькоИзбыток ТОГДА Остатки.КоличествоОстаток > 100
ИНАЧЕ ИСТИНА
КОНЕЦ
Проверены все возможные условия (нет "дыр" в логике)|Учтена обработка NULL-значений|Оптимизированы повторяющиеся вычисления в условиях|Тестировались пограничные случаи (нулевые значения, пустые строки)|-->
Типичные ошибки при работе с WHEN и как их избегать
Даже опытные разработчики допускают ошибки при работе с условной логикой в запросах. Вот наиболее распространённые проблемы и способы их решения:
1. Пропущенный блок ИНАЧЕ
Если не указать ИНАЧЕ, то для строк, не соответствующих ни одному условию, будет возвращаться NULL. Это часто приводит к неожиданным результатам при последующей обработке:
// Плохо - может вернуть NULL
ВЫБОР
КОГДА Сумма > 1000 ТОГДА "Крупный"
КОГДА Сумма > 100 ТОГДА "Средний"
КОНЕЦ КАК ТипЗаказа
// Хорошо - всегда возвращает строку
ВЫБОР
КОГДА Сумма > 1000 ТОГДА "Крупный"
КОГДА Сумма > 100 ТОГДА "Средний"
ИНАЧЕ "Мелкий"
КОНЕЦ КАК ТипЗаказа
2. Неправильная обработка NULL
Сравнения с NULL всегда возвращают UNKNOWN (неопределённость), что ломает логику WHEN:
// Это условие НИКОГДА не сработает для NULL
КОГДА Поле = NULL ТОГДА "Пустое"
// Правильный вариант
КОГДА Поле ЕСТЬ NULL ТОГДА "Пустое"
3. Избыточные вычисления в условиях
Если в нескольких условиях используется одно и то же сложное выражение, оно будет вычисляться многократно:
// Неэффективно - СУММА вычисляется 3 раза
ВЫБОР
КОГДА СУММА(Заказы.Сумма) > 1000000 ТОГДА "VIP"
КОГДА СУММА(Заказы.Сумма) > 500000 ТОГДА "Премиум"
ИНАЧЕ "Стандарт"
КОНЕЦ
// Оптимально - вынести в отдельное поле
ВЫБРАТЬ
СУММА(Заказы.Сумма) КАК ОбщаяСумма,
ВЫБОР
КОГДА ОбщаяСумма > 1000000 ТОГДА "VIP"
КОГДА ОбщаяСумма > 500000 ТОГДА "Премиум"
ИНАЧЕ "Стандарт"
КОНЕЦ КАК Категория
4. Неучтённая последовательность проверок
Условия проверяются сверху вниз, и если первое условие истинно, остальные не выполняются:
// Ошибка - "Средний" заказы никогда не попадут в эту категорию
ВЫБОР
КОГДА Сумма > 100 ТОГДА "Большой" // Сначала проверяется это
КОГДА Сумма > 1000 ТОГДА "Очень большой" // А это уже никогда не сработает
ИНАЧЕ "Мелкий"
КОНЕЦ
// Правильно - условия должны идти от более строгих к менее строгим
ВЫБОР
КОГДА Сумма > 1000 ТОГДА "Очень большой"
КОГДА Сумма > 100 ТОГДА "Большой"
ИНАЧЕ "Мелкий"
КОНЕЦ
Почему WHEN может тормозить запрос?
Сложные условия в WHEN заставляют СУБД строить полные планы выполнения для каждой ветки. Особенно критично это при использовании подзапросов в условиях. Для оптимизации выносите повторяющиеся вычисления в отдельные поля или используйте временные таблицы для промежуточных результатов.
WHEN в связке с агрегатными функциями и группировкой
Особенно мощно WHEN проявляет себя при работе с агрегатными функциями и группировкой данных. Это позволяет создавать сложные аналитические отчёты прямо в одном запросе.
Пример 1: Условная агрегация
Подсчёт количества заказов по категориям прямо в запросе:
ВЫБРАТЬ
Клиенты.Наименование КАК Клиент,
СУММА(ВЫБОР
КОГДА Заказы.СуммаДокумента > 100000 ТОГДА 1
ИНАЧЕ 0
КОНЕЦ) КАК КоличествоКрупныхЗаказов,
СУММА(ВЫБОР
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Отменен) ТОГДА 1
ИНАЧЕ 0
КОНЕЦ) КАК КоличествоОтмененныхЗаказов
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Клиент
СГРУППИРОВАТЬ ПО
Клиенты.Наименование
Пример 2: Динамическая группировка
Группировка данных по разным критериям в зависимости от параметра:
ВЫБРАТЬ
ВЫБОР
КОГДА &ГруппироватьПо = "Регион" ТОГДА Клиенты.Регион
КОГДА &ГруппироватьПо = "ТипКлиента" ТОГДА Клиенты.ТипКлиента
ИНАЧЕ "Общее"
КОНЕЦ КАК Группа,
СУММА(Заказы.СуммаДокумента) КАК Итого
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Клиент
СГРУППИРОВАТЬ ПО
ВЫБОР
КОГДА &ГруппироватьПо = "Регион" ТОГДА Клиенты.Регион
КОГДА &ГруппироватьПо = "ТипКлиента" ТОГДА Клиенты.ТипКлиента
ИНАЧЕ CONST("Общее", Строка)
КОНЕЦ
Пример 3: Условные вычисления в агрегатах
Расчёт средней суммы заказа только для определённых категорий:
ВЫБРАТЬ
Клиенты.Категория КАК КатегорияКлиента,
СРЕДНЕЕ(ВЫБОР
КОГДА Заказы.Дата > ДОБАВИТЬКДАТЕ(ТЕКУЩАЯДАТА(), МЕСЯЦ, -3) ТОГДА Заказы.СуммаДокумента
ИНАЧЕ NULL // NULL игнорируется при вычислении среднего
КОНЕЦ) КАК СредняяСуммаПоследнихЗаказов
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Клиент
СГРУППИРОВАТЬ ПО
Клиенты.Категория
При использовании WHEN внутри агрегатных функций помните, что NULL-значения игнорируются при вычислении СРЕДНЕЕ(), но учитываются в СУММА(). Это можно использовать для условного включения/исключения значений из расчётов.
Оптимизация запросов с множественными WHEN условиями
Запросы с большим количеством WHEN условий могут становиться "бутылочным горлышком" производительности. Вот ключевые приёмы оптимизации:
1. Вынос повторяющихся выражений
Если одно и то же выражение используется в нескольких условиях, вынесите его в отдельное поле:
ВЫБРАТЬ
Заказы.Номер КАК НомерЗаказа,
Заказы.Дата КАК ДатаЗаказа,
Заказы.СуммаДокумента КАК Сумма,
Заказы.СуммаДокумента / Заказы.КоличествоПозиций КАК СредняяЦенаПозиции,
ВЫБОР
КОГДА СредняяЦенаПозиции > 10000 ТОГДА "Премиум"
КОГДА СредняяЦенаПозиции > 5000 ТОГДА "Высокий"
КОГДА СредняяЦенаПозиции > 1000 ТОГДА "Средний"
ИНАЧЕ "Эконом"
КОНЕЦ КАК КатегорияЦены
ИЗ
Документ.ЗаказКлиента КАК Заказы
2. Использование временных таблиц
Для сложных условий с подзапросами имеет смысл разбить запрос на этапы:
// Этап 1: расчёт промежуточных данных
ВЫБРАТЬ
Клиенты.Ссылка КАК Клиент,
СУММА(Заказы.СуммаДокумента) КАК ОбщаяСумма,
МАКСИМУМ(Заказы.Дата) КАК ПоследняяДата
ПОМЕСТИТЬ ВТКлиенты
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Клиент
СГРУППИРОВАТЬ ПО
Клиенты.Ссылка
ИНДЕКСИРОВАТЬ ПО
Клиент
;
////////////////////////////////////////////////
// Этап 2: применение WHEN к подготовленным данным
ВЫБРАТЬ
Клиенты.Наименование КАК Клиент,
ВТКлиенты.ОбщаяСумма КАК СуммаЗаказов,
ВЫБОР
КОГДА ВТКлиенты.ОбщаяСумма > 1000000
И ВТКлиенты.ПоследняяДата > ДОБАВИТЬКДАТЕ(ТЕКУЩАЯДАТА(), МЕСЯЦ, -1) ТОГДА "Активный VIP"
КОГДА ВТКлиенты.ОбщаяСумма > 1000000 ТОГДА "VIP"
КОГДА ВТКлиенты.ПоследняяДата > ДОБАВИТЬКДАТЕ(ТЕКУЩАЯДАТА(), МЕСЯЦ, -1) ТОГДА "Активный"
ИНАЧЕ "Неактивный"
КОНЕЦ КАК СтатусКлиента
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ ВТКлиенты
ПО Клиенты.Ссылка = ВТКлиенты.Клиент
3. Оптимизация порядка условий
Располагайте условия от наиболее вероятных к наименее вероятным, и от самых "дешёвых" в вычислении к самым "дорогим":
// Неоптимально - сначала сложное условие с подзапросом
ВЫБОР
КОГДА ЕСТЬNULL(Заказы.Ссылка, ЛОЖЬ) И СУММА(Заказы.Сумма) > (ВЫБРАТЬ СРЕДНЕЕ(Сумма) ИЗ Документ.ЗаказКлиента) ТОГДА "Выше среднего"
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.Статусы.Отменен) ТОГДА "Отменён"
ИНАЧЕ "Standard"
КОНЕЦ
// Оптимально - сначала простое условие по статусу
ВЫБОР
КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.Статусы.Отменен) ТОГДА "Отменён"
КОГДА ЕСТЬNULL(Заказы.Ссылка, ЛОЖЬ) И СУММА(Заказы.Сумма) > (ВЫБРАТЬ СРЕДНЕЕ(Сумма) ИЗ Документ.ЗаказКлиента) ТОГДА "Выше среднего"
ИНАЧЕ "Standard"
КОНЕЦ
4. Замена WHEN на объединение запросов
В некоторых случаях несколько простых запросов с UNION ALL работают быстрее, чем один сложный с множеством условий:
// Вместо:
ВЫБРАТЬ
ВЫБОР
КОГДА Тип = 1 ТОГДА ...
КОГДА Тип = 2 ТОГДА ...
КОНЕЦ
ИЗ Таблица
// Можно сделать:
ВЫБРАТЬ ... ИЗ Таблица ГДЕ Тип = 1
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ... ИЗ Таблица ГДЕ Тип = 2
Для анализа производительности запросов с WHEN используйте план выполнения (включается через ПЛАН ЗАПРОСА перед основным текстом). Обратите внимание на операции TABLE SCAN — они часто указывают на неоптимальные условия.
WHEN в подзапросах и сложных конструкциях
Оператор WHEN можно успешно применять в подзапросах, что открывает дополнительные возможности для создания гибких запросов.
Пример 1: WHEN в подзапросе FROM
Создание динамической выборки на основе условий:
ВЫБРАТЬ
ОсновнойЗапрос.Клиент,
ОсновнойЗапрос.Сумма
ИЗ
(
ВЫБРАТЬ
Клиенты.Наименование КАК Клиент,
ВЫБОР
КОГДА &ТолькоАктивные ТОГДА СУММА(ВЫБОР КОГДА Заказы.Статус <> ЗНАЧЕНИЕ(Перечисление.Статусы.Отменен) ТОГДА Заказы.СуммаДокумента ИНАЧЕ 0 КОНЕЦ)
ИНАЧЕ СУММА(Заказы.СуммаДокумента)
КОНЕЦ КАК Сумма
ИЗ
Справочник.Клиенты КАК Клиенты
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента КАК Заказы
ПО Клиенты.Ссылка = Заказы.Клиент
СГРУППИРОВАТЬ ПО
Клиенты.Наименование
) КАК ОсновнойЗапрос
ГДЕ
ОсновнойЗапрос.Сумма > 0
Пример 2: WHEN в подзапросе WHERE
Фильтрация по сложным динамическим условиям:
ВЫБРАТЬ
Номенклатура.Наименование КАК Товар,
Остатки.КоличествоОстаток КАК Остаток
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ОстаткиТоваров.Остатки КАК Остатки
ПО Номенклатура.Ссылка = Остатки.Номенклатура
ГДЕ
Номенклатура.Ссылка В (
ВЫБРАТЬ РАЗЛИЧНЫЕ Номенклатура.Ссылка
ИЗ Справочник.Номенклатура КАК Номенклатура
ГДЕ
ВЫБОР
КОГДА &ТолькоАкционные ТОГДА Номенклатура.АкционныйТовар = ИСТИНА
КОГДА &ТолькоНовые ТОГДА Номенклатура.ДатаПоступления > ДОБАВИТЬКДАТЕ(ТЕКУЩАЯДАТА(), МЕСЯЦ, -1)
ИНАЧЕ ИСТИНА
КОНЕЦ
)
Пример 3: WHEN в вычисляемых полях подзапроса
Создание промежуточных вычисляемых полей с последующей фильтрацией:
ВЫБРАТЬ
Клиенты.Наименование КАК Клиент,
Результаты.Категория КАК КатегорияКлиента
ИЗ
(
ВЫБРАТЬ
Клиенты.Ссылка КАК Клиент,
ВЫБОР
КОГДА СУ