Условные поля в запросах 1С 8.3: как устроена конструкция ВЫБОР КОГДА ТОГДА ИНАЧЕ КОНЕЦ
Содержание
В языке запросов 1С условное поле пишется так: ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Это аналог CASE WHEN … THEN … ELSE … END из SQL. Ключевое слово именно ВЫБОР, а не «ВЫБРАТЬ»: ВЫБРАТЬ начинает сам запрос, и если написать «ВЫБРАТЬ КОГДА» внутри списка полей, платформа выдаст синтаксическую ошибку.
Конструкция работает в любой конфигурации на платформе 8.3 — в файловой и клиент-серверной базе, в консоли запросов, в конструкторе и в схеме компоновки данных.
Синтаксис ВЫБОР КОГДА ТОГДА: из чего состоит конструкция
Полная форма выглядит так:
ВЫБОР
КОГДА <Условие1> ТОГДА <Выражение1>
КОГДА <Условие2> ТОГДА <Выражение2>
...
ИНАЧЕ <ВыражениеПоУмолчанию>
КОНЕЦ КАК ИмяПоля
Как это читается:
- 🔹
КОГДА— логическое условие. Сравнение,МЕЖДУ,В,ПОДОБНО,ЕСТЬ NULL,ССЫЛКА, комбинации черезИ,ИЛИ,НЕ. - 🔹
ТОГДА— значение, которое получит поле, если условие истинно. Число, строка, дата, ссылка, булево, другое поле или выражение. - 🔹
ИНАЧЕ— значение, если не сработало ни одно условие. Необязательно, но без него в поле попадётNULL. - 🔹
КОНЕЦ— обязательное закрытие. Без него запрос не выполнится.
Условия проверяются сверху вниз. Сработало первое истинное — остальные уже не смотрятся. Поэтому порядок веток имеет значение, особенно для диапазонов.
В языке запросов 1С есть только «поисковая» форма: после КОГДА всегда идёт полное условие. Короткая SQL-форма вида CASE Поле WHEN 1 THEN … здесь не работает
— пишите КОГДА Поле = 1.
Простейший пример — подписать группы и элементы справочника:
ВЫБРАТЬ
Номенклатура.Наименование КАК Наименование,
ВЫБОР
КОГДА Номенклатура.ЭтоГруппа
ТОГДА "Группа"
ИНАЧЕ "Элемент"
КОНЕЦ КАК Вид
ИЗ
Справочник.Номенклатура КАК Номенклатура
Английский вариант синтаксиса тоже допустим: CASE WHEN … THEN … ELSE … END.
Где в запросе можно использовать оператор ВЫБОР
ВЫБОР — это выражение, поэтому его можно ставить почти везде, где допустимо выражение:
| Место в запросе | Зачем | Пример |
|---|---|---|
Список полей ВЫБРАТЬ | Вычисляемое поле, классификация | Статус «Просрочено / В срок» |
| Внутри агрегатной функции | Условные суммы, «шахматка» | СУММА(ВЫБОР КОГДА … ТОГДА Сумма ИНАЧЕ 0 КОНЕЦ) |
СГРУППИРОВАТЬ ПО | Группировка по вычисленной категории | Группировка по классу клиента |
УПОРЯДОЧИТЬ ПО | Свой порядок сортировки | Сначала срочные, потом остальные |
ГДЕ, ИМЕЮЩИЕ | Условный отбор | Допустимо, но обычно медленнее (см. оптимизацию) |
В СГРУППИРОВАТЬ ПО нельзя сослаться на псевдоним поля — повторите выражение ВЫБОР … КОНЕЦ целиком. В УПОРЯДОЧИТЬ ПО псевдоним использовать можно.
Конструкции можно вкладывать друг в друга: в ТОГДА или ИНАЧЕ — ещё один ВЫБОР. Но если вложенность глубже двух уровней, обычно проще развернуть всё в плоский список веток КОГДА.
Примеры ВЫБОР КОГДА для чисел, дат, ссылок и NULL
Числа: разбивка по диапазонам
Классифицируем реализации по сумме:
ВЫБРАТЬ
Реализация.Ссылка КАК Документ,
Реализация.СуммаДокумента КАК Сумма,
ВЫБОР
КОГДА Реализация.СуммаДокумента >= 500000
ТОГДА "Крупная"
КОГДА Реализация.СуммаДокумента >= 50000
ТОГДА "Средняя"
ИНАЧЕ "Мелкая"
КОНЕЦ КАК Категория
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Проведен
Документ на 120 000 ₽ не проходит первую ветку, проходит вторую и получает «Средняя». Документ на 30 000 ₽ уходит в ИНАЧЕ.
Даты: просрочка в днях
Функция РАЗНОСТЬДАТ считает разницу между датами в нужных единицах. С ней удобно строить «возрастные» корзины:
ВЫБОР
КОГДА РАЗНОСТЬДАТ(Док.Дата, &ДатаОтчета, ДЕНЬ) <= 30
ТОГДА "до 30 дней"
КОГДА РАЗНОСТЬДАТ(Док.Дата, &ДатаОтчета, ДЕНЬ) <= 90
ТОГДА "31–90 дней"
ИНАЧЕ "свыше 90 дней"
КОНЕЦ КАК Срок
Документ от 1 марта при дате отчёта 15 апреля даёт 45 дней — попадает во вторую корзину.
Ссылки и перечисления
Значения перечислений и предопределённых элементов в условии задаются через ЗНАЧЕНИЕ():
ВЫБОР
КОГДА Контрагенты.ЮридическоеФизическоеЛицо = ЗНАЧЕНИЕ(Перечисление.ЮридическоеФизическоеЛицо.ФизическоеЛицо)
ТОГДА "Физлицо"
ИНАЧЕ "Организация"
КОНЕЦ КАК ТипКонтрагента
Имена реквизитов и перечислений здесь — из Бухгалтерии предприятия 3.0. В другой конфигурации проверьте их в дереве метаданных конфигуратора.
Для полей составного типа (например, Регистратор в регистре) тип проверяют через ССЫЛКА:
ВЫБОР
КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
ТОГДА "Приход"
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА "Расход"
ИНАЧЕ "Прочее"
КОНЕЦ КАК Направление
NULL после левого соединения
После ЛЕВОЕ СОЕДИНЕНИЕ в полях правой таблицы может оказаться NULL. Сравнение = NULL никогда не даёт истину, нужен ЕСТЬ NULL:
ВЫБОР
КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL
ТОГДА 0
ИНАЧЕ Остатки.КоличествоОстаток
КОНЕЦ КАК Остаток
Для такой простой замены короче функция ЕСТЬNULL(Остатки.КоличествоОстаток, 0). ВЫБОР нужен, когда при NULL надо сделать что-то сложнее, чем подставить одно значение.
Условные суммы: «шахматка» по месяцам
ВЫБОР внутри агрегатной функции превращает строки в колонки:
ВЫБРАТЬ
Реализация.Контрагент КАК Контрагент,
СУММА(ВЫБОР КОГДА МЕСЯЦ(Реализация.Дата) = 1 ТОГДА Реализация.СуммаДокумента ИНАЧЕ 0 КОНЕЦ) КАК Январь,
СУММА(ВЫБОР КОГДА МЕСЯЦ(Реализация.Дата) = 2 ТОГДА Реализация.СуммаДокумента ИНАЧЕ 0 КОНЕЦ) КАК Февраль,
СУММА(ВЫБОР КОГДА МЕСЯЦ(Реализация.Дата) = 3 ТОГДА Реализация.СуммаДокумента ИНАЧЕ 0 КОНЕЦ) КАК Март
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Проведен
И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Реализация.Контрагент
ИНАЧЕ 0 здесь важен: без него в «пустых» месяцах вместо нуля будет NULL, и в выгрузке появятся пустые ячейки.
ВЫБОР в связке с МЕЖДУ, В и ПОДОБНО
МЕЖДУ: границы включаются
МЕЖДУ А И Б включает обе границы. Если диапазоны стыкуются, граничное значение уйдёт в первую подходящую ветку:
ВЫБОР
КОГДА Сумма МЕЖДУ 0 И 10000 ТОГДА "до 10 тыс."
КОГДА Сумма МЕЖДУ 10000 И 50000 ТОГДА "10–50 тыс."
ИНАЧЕ "свыше 50 тыс."
КОНЕЦ
Сумма ровно 10 000 попадёт в «до 10 тыс.», хотя по смыслу могла бы во вторую группу. Чтобы не гадать, используйте пары >= и <: КОГДА Сумма >= 10000 И Сумма < 50000.
С датами та же ловушка: Дата МЕЖДУ &Начало И &Конец, где &Конец — дата без времени, отрежет документы, созданные днём последнего числа. Передавайте конец периода через КонецДня() или КонецМесяца().
В: проверка по списку
ВЫБОР
КОГДА Реализация.Контрагент В (&КлючевыеКлиенты)
ТОГДА "Ключевой"
ИНАЧЕ "Обычный"
КОНЕЦ КАК Статус
В параметр &КлючевыеКлиенты передают массив или список значений. Короткий фиксированный список можно написать прямо в тексте: КОГДА МЕСЯЦ(Дата) В (1, 2, 3) ТОГДА "I квартал".
Если список большой и живёт в справочнике или регистре, не тащите его в В. Соедините таблицы и проверяйте поле присоединённой таблицы на ЕСТЬ NULL — так запрос понятнее и стабильнее по скорости.
ПОДОБНО: классификация по тексту
ВЫБОР
КОГДА Номенклатура.Наименование ПОДОБНО "%доставк%"
ТОГДА "Логистика"
КОГДА Номенклатура.Наименование ПОДОБНО "%монтаж%"
ТОГДА "Работы"
ИНАЧЕ "Товары"
КОНЕЦ КАК Направление
Шаблоны: % — любое количество любых символов, _ — ровно один символ. Если в тексте нужно найти сам символ % или _, задайте спецсимвол: ПОДОБНО "%100!%%" СПЕЦСИМВОЛ "!".
Классификация по наименованию хрупкая: одна опечатка в карточке — и позиция уходит в «Товары». Для отчётов, на которые смотрит руководство, лучше завести реквизит или группу и проверять их.
Отчёт с классификацией клиентов: пример целиком
Задача: разбить покупателей на классы по выручке за период и показать их в порядке важности. Делаем в два шага через временную таблицу — сначала считаем выручку, потом классифицируем уже готовые суммы.
ВЫБРАТЬ
Реализация.Контрагент КАК Контрагент,
СУММА(Реализация.СуммаДокумента) КАК Выручка
ПОМЕСТИТЬ ВТ_Выручка
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Проведен
И Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Реализация.Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Выручка.Контрагент КАК Контрагент,
ВТ_Выручка.Выручка КАК Выручка,
ВЫБОР
КОГДА ВТ_Выручка.Выручка >= 1000000
ТОГДА "A — ключевой"
КОГДА ВТ_Выручка.Выручка >= 300000
ТОГДА "B — стабильный"
ИНАЧЕ "C — разовый"
КОНЕЦ КАК Класс,
ВЫБОР
КОГДА ВТ_Выручка.Выручка >= 1000000
ТОГДА 1
КОГДА ВТ_Выручка.Выручка >= 300000
ТОГДА 2
ИНАЧЕ 3
КОНЕЦ КАК ПорядокКласса
ИЗ
ВТ_Выручка КАК ВТ_Выручка
УПОРЯДОЧИТЬ ПО
ПорядокКласса,
Выручка УБЫВ
Что здесь сделано осознанно:
- Агрегация отдельно от классификации. Класс зависит от итоговой суммы, а не от отдельного документа. Поэтому
ВЫБОРстоит во втором запросе, по готовой выручке. - Второе поле
ПорядокКласса. Сортировка по строке «A — ключевой» сработает, пока названия начинаются с букв по алфавиту. Числовой порядок не сломается, если завтра классы переименуют. - Пороги не дублируются в разных местах кода. Если порогов становится больше, вынесите их в параметры
&ПорогA,&ПорогB— тогда их можно менять без правки текста запроса.
Пример: клиент с выручкой 1 250 000 ₽ получит класс A и номер 1, клиент с 420 000 ₽ — B и 2, клиент с 80 000 ₽ — C и 3.
Вызов из встроенного языка:
Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ДатаОтчета));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ДатаОтчета));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Строка(Выборка.Контрагент) + ": " + Выборка.Класс);
КонецЦикла;
Чтобы получить сводку «сколько клиентов в каждом классе», добавьте третий запрос к ВТ_Выручка с КОЛИЧЕСТВО(Контрагент) и группировкой — и повторите в СГРУППИРОВАТЬ ПО выражение ВЫБОР целиком.
Как ускорить запрос с ВЫБОР КОГДА
Сам по себе ВЫБОР в списке полей почти бесплатен: СУБД вычисляет его для уже отобранных строк. Тормоза начинаются, когда условие стоит там, где СУБД отбирает или соединяет данные.
- 🐢
ВЫБОРвГДЕ. Условие видаГДЕ ВЫБОР КОГДА … КОНЕЦ = 1не даёт использовать индекс — СУБД перебирает все строки. Перепишите его в обычные условия черезИиИЛИ. - 🐢
ВЫБОРв условии соединения.СОЕДИНЕНИЕ … ПО ВЫБОР …— частая причина запросов на минуты. Подготовьте нужное поле во временной таблице и соединяйте по нему. - 🐢 Отбор не в параметрах виртуальной таблицы. Для
Остатки()иОбороты()период и отбор по измерениям задавайте в параметрах таблицы, а не черезГДЕи тем более не черезВЫБОР. - 🐢 Разыменование составного поля.
Движения.Регистратор.КонтрагентвнутриВЫБОРзаставляет платформу соединять таблицы всех документов, которые могут быть регистратором. Приведите тип:ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент. - 🐢 Одно и то же выражение много раз. Если
РАЗНОСТЬДАТ(…)повторяется в пяти ветках и ещё в группировке, посчитайте его один раз во временной таблице и классифицируйте уже готовое поле.
Пример переписывания условия из ГДЕ:
// Медленно: индекс по дате не используется
ГДЕ
ВЫБОР
КОГДА &ТолькоТекущийМесяц
ТОГДА Док.Дата >= &НачалоМесяца
ИНАЧЕ ИСТИНА
КОНЕЦ
// Быстрее: то же условие обычной логикой
ГДЕ
(НЕ &ТолькоТекущийМесяц ИЛИ Док.Дата >= &НачалоМесяца)
Ещё надёжнее — собрать текст запроса в коде и добавлять условие по дате только тогда, когда отбор действительно нужен.
Условия ВЫБОР в СКД и во встроенном языке
В схеме компоновки данных ВЫБОР работает в двух местах:
- 📊 В тексте запроса набора данных — ровно как в обычном запросе.
- 📊 В вычисляемых полях и выражениях ресурсов — язык выражений СКД понимает
Выбор Когда … Тогда … Иначе … Конец.
Вычисляемое поле СКД считается уже после получения данных. Для классификации по итоговым суммам это удобно, но отбор по такому полю не уходит в СУБД. Если отбирать нужно по классу, лучше рассчитать его в запросе.
Во встроенном языке модулей конструкции ВЫБОР нет. Её роль играют:
Если Сумма >= 500000 Тогда
Категория = "Крупная";
ИначеЕсли Сумма >= 50000 Тогда
Категория = "Средняя";
Иначе
Категория = "Мелкая";
КонецЕсли;
// Для двух вариантов — тернарный оператор
Вид = ?(ЭтоГруппа, "Группа", "Элемент");
Классификацию большого набора строк лучше делать в запросе. Обработка той же логики в цикле по выборке на десятках тысяч строк заметно медленнее.
Ошибки при работе с ВЫБОР КОГДА и как их найти
Ещё два тихих источника ошибок, на которые запрос не ругается:
- ⚠️ Сравнение с NULL через «=».
КОГДА Поле = NULLвсегда ложно — используйтеЕСТЬ NULL. - ⚠️ Строковые литералы в результате. Если поле потом сравнивают в коде со строкой, а в запросе текст изменили («Крупная» → «Крупный клиент»), код молча перестанет находить совпадения. Для логики лучше возвращать числа или значения перечислений, а строки — только для показа.
Частые вопросы
Можно ли в ВЫБОР вернуть булево значение?
ВЫБОР КОГДА … ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ. Часто то же самое проще записать самим условием без ВЫБОР — оно уже возвращает булево.