Условные выражения в запросах 1С 8.3: как устроена конструкция ВЫБОР и где она подводит
Содержание
ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ — условное выражение языка запросов 1С 8.3, аналог CASE WHEN … THEN … ELSE … END из SQL. Оно вычисляет значение поля прямо в запросе: подставляет текст вместо кода, раскладывает суммы по колонкам, задаёт свой порядок сортировки.
Конструкция работает только внутри текста запроса и в выражениях системы компоновки данных (СКД). В модулях встроенного языка её нет: там пишут Если … ИначеЕсли … Иначе … КонецЕсли или короткую форму ?(Условие, Значение1, Значение2).
Как записывается ВЫБОР КОГДА ТОГДА ИНАЧЕ КОНЕЦ
Полная форма выражения:
ВЫБОР
КОГДА <Условие1> ТОГДА <Результат1>
КОГДА <Условие2> ТОГДА <Результат2>
ИНАЧЕ <РезультатПоУмолчанию>
КОНЕЦ
Английский вариант того же текста: CASE WHEN … THEN … ELSE … END. Платформа понимает оба написания; в типовых конфигурациях принято русское.
Правила, которые надо держать в голове:
- 🔹 веток
КОГДАможет быть сколько угодно, но хотя бы одна обязательна; - 🔹 условия проверяются сверху вниз, срабатывает первая истинная ветка, остальные уже не смотрятся;
- 🔹
ИНАЧЕнеобязательна — без неё при всех ложных условиях выражение вернётNULL; - 🔹 выражение всегда закрывается словом
КОНЕЦ, даже вложенное; - 🔹 после
КОНЕЦполю почти всегда дают имя черезКАК, иначе платформа назовёт колонку сама (Поле1,Поле2), и обращаться к ней из кода неудобно.
Простейший пример — показать признак пометки удаления словами:
ВЫБРАТЬ
Контрагенты.Наименование КАК Наименование,
ВЫБОР
КОГДА Контрагенты.ПометкаУдаления
ТОГДА "Помечен на удаление"
ИНАЧЕ "Действует"
КОНЕЦ КАК Состояние
ИЗ
Справочник.Контрагенты КАК Контрагенты
В SQL есть короткая форма CASE Поле WHEN 1 THEN …, где поле пишут один раз. В языке запросов 1С пользуйтесь полной формой: условие целиком в каждой ветке (КОГДА Поле = 1 ТОГДА …). Так текст читается однозначно и его без ошибок разбирает конструктор запроса.
Где выражение допустимо
ВЫБОР — обычное выражение, поэтому его ставят везде, где запрос принимает выражение:
| Часть запроса | Зачем там ВЫБОР |
|---|---|
ВЫБРАТЬ | вычисляемое поле: текст, число, признак |
внутри СУММА, КОЛИЧЕСТВО, МАКСИМУМ | условная агрегация, суммы по колонкам |
ГДЕ и ИМЕЮЩИЕ | условие, зависящее от другого поля или параметра |
СГРУППИРОВАТЬ ПО | группировка по вычисленной категории |
УПОРЯДОЧИТЬ ПО | нестандартный порядок строк |
условие СОЕДИНЕНИЕ … ПО | технически можно, но почти всегда дорого |
В конструкторе запроса выражение вводят в окне произвольного выражения поля: на вкладке «Таблицы и поля» добавьте поле и вместо выбора реквизита напишите текст вручную.
ВЫБОР или ЕСЛИ: что брать в запросе и что в коде
Частый вопрос новичков звучит как «чем заменить ЕСЛИ в запросе». Ответ прямой: в языке запросов оператора ЕСЛИ нет, единственная условная конструкция — ВЫБОР. Обратное тоже верно: в модуле нельзя написать ВЫБОР КОГДА.
Где что применять:
| Задача | Инструмент |
|---|---|
| значение зависит от полей таблицы, обрабатывается много строк | ВЫБОР в запросе |
| логика нужна в вычисляемом поле отчёта на СКД | ВЫБОР в выражении поля СКД |
| нужно вызвать функцию общего модуля, обратиться к объекту, записать данные | Если в коде |
| одно значение по одному условию в коде | ?(Условие, А, Б) |
| ветвление по многим условиям в коде | Если … ИначеЕсли … КонецЕсли |
Главный довод в пользу запроса — объём данных. Посчитать категорию для 200 000 строк остатков на стороне СУБД заметно быстрее, чем выгрузить их в таблицу значений и перебрать циклом. А ещё запрос с ВЫБОР сразу отдаёт готовую колонку в отчёт: не нужен лишний проход по результату.
Код выигрывает, когда условие требует данных вне запроса: настроек пользователя, результатов внешнего сервиса, сложных вычислений. Такие вещи в текст запроса не перенести.
Сложные условия, параметры и вложенные ВЫБОР
В ветке КОГДА стоит любое логическое выражение. Можно комбинировать И, ИЛИ, НЕ, сравнения, МЕЖДУ, В (…), ПОДОБНО, ЕСТЬ NULL, ССЫЛКА.
Условие по типу ссылки
Поле Регистратор регистра ссылается на документы разных видов. ВЫБОР с оператором ССЫЛКА превращает его в понятную колонку:
ВЫБОР
КОГДА Продажи.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА "Отгрузка"
КОГДА Продажи.Регистратор ССЫЛКА Документ.ВозвратТоваровОтПокупателя
ТОГДА "Возврат"
ИНАЧЕ "Прочее"
КОНЕЦ КАК ВидОперации
Тот же результат даёт сравнение ТИПЗНАЧЕНИЯ(Поле) = ТИП(Документ.РеализацияТоваровУслуг). Имена документов в примерах — из типовых конфигураций; в своей базе подставьте те, что есть в дереве метаданных.
Условие по параметру
Параметр в условии работает как переключатель отчёта:
ВЫБОР
КОГДА &ВВалюте
ТОГДА Остатки.СуммаВалютнаяОстаток
ИНАЧЕ Остатки.СуммаОстаток
КОНЕЦ КАК Сумма
Параметр ВВалюте передают из кода через Запрос.УстановитьПараметр("ВВалюте", Истина). Одна текстовка запроса обслуживает оба режима.
Вложенный ВЫБОР
Внутри ТОГДА и ИНАЧЕ допускается ещё один ВЫБОР. Каждое вложенное выражение закрывается своим КОНЕЦ:
ВЫБОР
КОГДА Т.Сумма >= 1000000 ТОГДА
ВЫБОР
КОГДА Т.Просрочено ТОГДА "Крупный, просрочен"
ИНАЧЕ "Крупный"
КОНЕЦ
ИНАЧЕ "Обычный"
КОНЕЦ КАК Группа
Больше двух уровней вложенности читать уже тяжело. Чаще всего вложенность разворачивается в плоский список веток с условием через И — порядок веток сделает ту же работу.
Типичные ошибки в конструкции ВЫБОР
Отдельная ловушка — типы. Если ветки возвращают значения разных типов (строку в одной, число в другой), колонка получает составной тип. Отчёт это переживёт, но сортировка и сравнения дальше по запросу начнут вести себя непредсказуемо. Держите все ветки в одном типе, а при необходимости явно приводите через ВЫРАЗИТЬ.
Ещё одна — пустые ссылки. Пустой контрагент в документе и отсутствие строки в левом соединении — разные вещи. Первое ловится сравнением со ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка), второе — через ЕСТЬ NULL. Если нужны оба случая, пишите обе проверки в одной ветке через ИЛИ.
Как ВЫБОР влияет на скорость запроса
В списке полей ВЫБОР почти ничего не стоит: СУБД вычисляет выражение для уже отобранных строк. Проблемы начинаются, когда выражение оказывается в отборе или соединении.
Почему так. Индекс помогает, только когда условие сравнивает само поле с значением. Выражение вида ГДЕ ВЫБОР КОГДА … КОНЕЦ = … скрывает поле внутри вычисления, и СУБД приходится считать его для каждой строки таблицы.
Классический пример «универсального» отбора:
ГДЕ
ВЫБОР
КОГДА &Склад = ЗНАЧЕНИЕ(Справочник.Склады.ПустаяСсылка)
ТОГДА ИСТИНА
ИНАЧЕ Остатки.Склад = &Склад
КОНЕЦ
Работать будет, но индекс по складу, скорее всего, не используется — даже когда склад задан. На маленькой базе разницы не видно, на регистре с миллионами записей запрос заметно тормозит.
Что сделать вместо этого:
- Собрать текст запроса в коде: добавлять строку
И Остатки.Склад = &Складтолько когда склад выбран. - В отчёте на СКД вынести отбор в настройки — компоновщик сам добавит условие, только если пользователь его включил.
- Для виртуальных таблиц остатков и оборотов ставить условие в параметры таблицы, а не в
ГДЕ.
В условии соединения ВЫБОР — самый дорогой вариант. Если ключ соединения зависит от условия, лучше разбить запрос на две части и склеить через ОБЪЕДИНИТЬ ВСЕ, либо подготовить ключ во временной таблице и проиндексировать её.
Временная таблица вообще хорошо снимает нагрузку: посчитайте категорию один раз, положите в ПОМЕСТИТЬ, добавьте ИНДЕКСИРОВАТЬ ПО и соединяйтесь уже по готовому полю. Сравнивать варианты стоит на реальных данных — замером времени или в технологическом журнале, а не на глаз.
Когда лучше обойтись без ВЫБОР
ВЫБОР — не единственный инструмент, и иногда он только мешает.
| Задача | Вместо ВЫБОР | Почему |
|---|---|---|
| заменить NULL на значение | ЕСТЬNULL(Поле, 0) | короче и понятнее |
| показать имя ссылки | ПРЕДСТАВЛЕНИЕ(Поле) | платформа сама возьмёт представление |
| перевести коды в названия, вариантов 20+ | справочник или временная таблица с соединением | список веток неудобно поддерживать |
| разные отборы под разные режимы | собрать текст запроса в коде | индексы работают |
| разные ключи соединения | ОБЪЕДИНИТЬ ВСЕ двух запросов | каждый запрос простой и быстрый |
| логика с вызовом функций | обработка результата в коде | функции модулей в запросе недоступны |
Простое правило: если ветки ВЫБОР описывают справочные данные (коды, ставки, соответствия), им место в таблице, а не в тексте запроса. Завтра добавится новое значение, и править придётся код вместо одной записи в справочнике.
Примеры ВЫБОР КОГДА ИНАЧЕ для типовых задач
Приход и расход из одного регистра
Регистр накопления хранит движения с видом «Приход» или «Расход». Разложить их по двум колонкам:
ВЫБРАТЬ
Движения.Номенклатура КАК Номенклатура,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Приход,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Расход
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
Имя регистра условное — подставьте свой. Для готовых оборотов проще взять виртуальную таблицу .Обороты, где поля прихода и расхода уже есть; приём с условной суммой нужен, когда считаете по физической таблице или по своим признакам.
Возраст задолженности
Разбить долг по срокам относительно даты отчёта:
ВЫБОР
КОГДА РАЗНОСТЬДАТ(Долги.ДатаДокумента, &ДатаОтчета, ДЕНЬ) <= 30
ТОГДА "до 30 дней"
КОГДА РАЗНОСТЬДАТ(Долги.ДатаДокумента, &ДатаОтчета, ДЕНЬ) <= 60
ТОГДА "31–60 дней"
КОГДА РАЗНОСТЬДАТ(Долги.ДатаДокумента, &ДатаОтчета, ДЕНЬ) <= 90
ТОГДА "61–90 дней"
ИНАЧЕ "больше 90 дней"
КОНЕЦ КАК Интервал
Здесь работает порядок веток. Документ от 15 января при дате отчёта 1 марта даёт 45 дней: первая ветка ложна, вторая истинна — строка уходит в «31–60 дней». Нижнюю границу в каждой ветке писать не нужно.
Свой порядок сортировки
Показать сначала просроченные заказы, затем текущие, в конце закрытые:
УПОРЯДОЧИТЬ ПО
ВЫБОР
КОГДА Заказы.Просрочен ТОГДА 1
КОГДА НЕ Заказы.Закрыт ТОГДА 2
ИНАЧЕ 3
КОНЕЦ,
Заказы.Дата
Поля Просрочен и Закрыт условные: в вашей таблице это могут быть статус или дата закрытия. Суть приёма — числовой ключ сортировки, которого в данных нет.
Защита от деления на ноль
Цена за единицу при нулевом количестве:
ВЫБОР
КОГДА Т.Количество = 0 ТОГДА 0
ИНАЧЕ Т.Сумма / Т.Количество
КОНЕЦ КАК Цена
Без этой проверки запрос на строке с нулевым количеством упадёт с ошибкой деления на ноль, и отчёт не сформируется целиком.
Флажок для отбора в отчёте
ВЫБОР может возвращать ИСТИНА или ЛОЖЬ. Колонка-признак удобна в СКД: пользователь ставит отбор «ЕстьОстатокДолга = Да» в настройках, не разбираясь в суммах.
ВЫБОР
КОГДА Расчеты.СуммаОстаток > 0 ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК ЕстьОстатокДолга
Такое поле проще написать как Расчеты.СуммаОстаток > 0 КАК ЕстьОстатокДолга — логическое выражение в списке полей допустимо. ВЫБОР здесь нужен, только если веток больше двух или итог не логический.