Конструкция ВЫБОР в языке запросов 1С: ветвления, вложенность, ошибки и скорость
Содержание
Условие в запросе 1С пишется конструкцией ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Это аналог CASE WHEN из SQL: платформа проверяет ветки КОГДА сверху вниз и возвращает значение первой истинной. Если ни одна не сработала и ИНАЧЕ нет, результат равен NULL.
Оператор ВЫБОР работает в любом месте запроса, где допустимо выражение: в списке полей, в ГДЕ, в условии соединения, в СГРУППИРОВАТЬ ПО, УПОРЯДОЧИТЬ ПО и внутри агрегатных функций. Отдельной функции КОГДА в языке запросов нет: КОГДА — это ключевое слово ветки внутри ВЫБОР. Синтаксис одинаков для всех конфигураций на платформе 8.3 — БП, ЗУП, УТ, ERP.
Синтаксис оператора ВЫБОР по частям
Полная форма выглядит так:
ВЫБОР
КОГДА Условие1 ТОГДА Значение1
КОГДА Условие2 ТОГДА Значение2
ИНАЧЕ ЗначениеПоУмолчанию
КОНЕЦ КАК ИмяПоля
Из чего она состоит:
- 🔹
ВЫБОР— открывает конструкцию. Английский вариант —CASE. - 🔹
КОГДА … ТОГДА …— ветка. Веток может быть сколько угодно, минимум одна. Английский вариант —WHEN … THEN …. - 🔹
ИНАЧЕ— значение, если ни одно условие не выполнено. Необязательна. Английский вариант —ELSE. - 🔹
КОНЕЦ— закрывает конструкцию. Без него запрос не разберётся. Английский вариант —END. - 🔹
КАК ИмяПоля— псевдоним. Для поля выборки без него платформа назовёт колонку сама, и обращаться к ней из кода будет неудобно.
Что важно знать о форме записи
В языке запросов 1С есть только «поисковая» форма: после каждого КОГДА стоит полноценное логическое условие. Сокращённой записи вида ВЫБОР Поле КОГДА 1 ТОГДА …, как в некоторых диалектах SQL, нет. Каждый раз пишите поле заново: КОГДА Поле = 1 ТОГДА ….
Регистр ключевых слов значения не имеет. Выбор Когда … Конец и ВЫБОР КОГДА … КОНЕЦ равнозначны. Заглавные буквы — просто привычка, которая помогает читать длинный текст запроса.
Ветки проверяются по порядку, и срабатывает только первая истинная. Если условия пересекаются, ставьте самое узкое выше. Иначе широкое условие «съест» все строки.
Простые условия в ВЫБОР: сравнение значений
Самый частый сценарий — превратить значение поля в понятную подпись или число.
Сравнение с числом и строкой
ВЫБРАТЬ
Товары.Номенклатура,
Товары.Количество,
ВЫБОР
КОГДА Товары.Количество = 0 ТОГДА "Нет в наличии"
КОГДА Товары.Количество < 10 ТОГДА "Мало"
ИНАЧЕ "Достаточно"
КОНЕЦ КАК Остаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК Товары
Строка с количеством 5 попадёт во вторую ветку. Строка с количеством 0 — в первую, хотя условие «меньше 10» для неё тоже истинно: первая ветка стоит выше.
Сравнение со ссылками и перечислениями
Для предопределённых значений используйте функцию ЗНАЧЕНИЕ():
ВЫБОР
КОГДА Документ.ВидОперации = ЗНАЧЕНИЕ(Перечисление.ВидыОперацийРеализацияТоваров.Товары)
ТОГДА 1
ИНАЧЕ 0
КОНЕЦ
Имя перечисления выше — пример. Точное имя смотрите в дереве конфигурации: в разных конфигурациях перечисления называются по-разному.
Для пустой ссылки — та же функция: ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).
Проверка типа: ССЫЛКА
Если поле составного типа — например, регистратор регистра — ветку можно строить по типу значения:
ВЫБОР
КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг ТОГДА "Приход"
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг ТОГДА "Расход"
ИНАЧЕ "Прочее"
КОНЕЦ КАК ТипДвижения
Сравнение с NULL
Здесь ошибаются чаще всего. Условие КОГДА Поле = NULL никогда не будет истинным: сравнение с NULL через знак равенства не работает
. Правильная запись — ЕСТЬ NULL:
ВЫБОР
КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL ТОГДА 0
ИНАЧЕ Остатки.КоличествоОстаток
КОНЕЦ
NULL появляется после левого соединения, когда справа не нашлось пары. Его же получите, если у ВЫБОР нет ИНАЧЕ и ни одна ветка не сработала.
Составные логические выражения внутри КОГДА
После КОГДА можно писать всё, что допустимо в ГДЕ. Это и операторы И, ИЛИ, НЕ, и проверки В, МЕЖДУ, ПОДОБНО.
| Оператор | Пример условия | Когда удобен |
|---|---|---|
И | КОГДА Сумма > 0 И Оплачено = ЛОЖЬ | Нужно одновременно несколько признаков |
ИЛИ | КОГДА Склад = &Склад1 ИЛИ Склад = &Склад2 | Альтернативные значения |
НЕ | КОГДА НЕ Проведен | Отрицание булева поля |
В | КОГДА Статус В (&СписокСтатусов) | Список значений из параметра |
МЕЖДУ | КОГДА Дата МЕЖДУ &Начало И &Конец | Диапазон дат или чисел |
ПОДОБНО | КОГДА Наименование ПОДОБНО "%услуг%" | Поиск по части строки |
В ИЕРАРХИИ | КОГДА Номенклатура В ИЕРАРХИИ (&Группа) | Принадлежность группе справочника |
Скобки и приоритет
И выполняется раньше, чем ИЛИ. Условие А ИЛИ Б И В читается как А ИЛИ (Б И В). Если вы имели в виду другое — ставьте скобки явно. Лишние скобки ничего не стоят, а потерянные дают неправильный результат без всякой ошибки.
Пример: сегментация покупателей
ВЫБОР
КОГДА Продажи.Сумма >= 1000000
И Продажи.КоличествоДокументов >= 12 ТОГДА "Ключевой"
КОГДА Продажи.Сумма >= 1000000
ИЛИ Продажи.КоличествоДокументов >= 12 ТОГДА "Крупный"
КОГДА Продажи.Сумма > 0 ТОГДА "Обычный"
ИНАЧЕ "Без продаж"
КОНЕЦ КАК Сегмент
Покупатель с оборотом 1 200 000 и 15 документами попадёт в «Ключевые». С оборотом 1 200 000 и 3 документами — в «Крупные». С оборотом 50 000 и 2 документами — в «Обычные».
Вложенный ВЫБОР: когда он нужен
Внутри ТОГДА и ИНАЧЕ может стоять ещё один ВЫБОР. Глубина вложенности не ограничена, но читать такой текст быстро становится трудно.
ВЫБОР
КОГДА Документы.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг ТОГДА
ВЫБОР
КОГДА Документы.Сумма > 100000 ТОГДА "Крупная отгрузка"
ИНАЧЕ "Отгрузка"
КОНЕЦ
ИНАЧЕ "Прочее"
КОНЕЦ КАК Вид
Как развернуть вложенность в плоский список
Почти любой вложенный ВЫБОР переписывается одним уровнем — за счёт порядка веток и условия через И:
ВЫБОР
КОГДА Документы.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
И Документы.Сумма > 100000 ТОГДА "Крупная отгрузка"
КОГДА Документы.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА "Отгрузка"
ИНАЧЕ "Прочее"
КОНЕЦ КАК Вид
Результат тот же. Плоский вариант проще читать и проще дополнять новой веткой.
Вложенность оправдана в двух случаях:
- 📌 Внешнее условие тяжёлое — например, длинный список типов, — и повторять его в каждой ветке неудобно.
- 📌 Внутренний ВЫБОР стоит внутри арифметики:
Сумма * ВЫБОР … КОНЕЦ.
ВЫБОР внутри агрегатных функций
Это приём, который заменяет несколько запросов одним. Нужны приход и расход в разных колонках — оборачиваете ВЫБОР в СУММА:
ВЫБРАТЬ
Движения.Номенклатура,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Приход,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Расход
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
Регистр в примере условный, подставьте свой. Смысл приёма: одна выборка, одна группировка, две колонки. То же работает с КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВЫБОР …) — если в ИНАЧЕ поставить NULL, эти строки не посчитаются.
ВЫБОР, КОГДА и другие способы задать условие
Путаница «ВЫБОР или КОГДА» обычно идёт из встроенного языка. Там есть тернарный оператор ?(Условие, А, Б) и конструкция Если … Тогда … КонецЕсли. В тексте запроса их нет. Вот как соотносятся инструменты:
| Инструмент | Где работает | Что делает | Ограничение |
|---|---|---|---|
ВЫБОР КОГДА … ТОГДА … КОНЕЦ | Язык запросов | Любое ветвление | Многословен |
КОГДА | Только внутри ВЫБОР | Начинает ветку | Сам по себе не используется |
ЕСТЬNULL(Поле, Замена) | Язык запросов | Подменяет NULL | Только одна проверка — на NULL |
?(Условие, А, Б) | Встроенный язык | Тернарный выбор | В запросе вызывает синтаксическую ошибку |
Выбор Когда … Конец в вычисляемом поле | СКД | Ветвление в выражениях схемы | Работает над результатом, не над таблицами базы |
Условие в ГДЕ | Язык запросов | Отбрасывает строки | Не меняет значения, только фильтрует |
ЕСТЬNULL или ВЫБОР
Для замены NULL короче писать ЕСТЬNULL(Остатки.КоличествоОстаток, 0). Это то же самое, что ВЫБОР с проверкой ЕСТЬ NULL, но в одну строку. ВЫБОР нужен, когда подмена зависит от других полей или значений больше двух.
Фильтр или ветвление
Если строки с нулевой суммой вам просто не нужны — пишите ГДЕ Сумма <> 0. ВЫБОР в этом случае лишний. Он нужен, когда строку надо оставить, но показать в ней другое значение.
Типичные ошибки в условиях запроса и как их отлаживать
Синтаксические
- ❌ Забыли
КОНЕЦ. Платформа сообщит о синтаксической ошибке и укажет место, обычно на следующем после ВЫБОР ключевом слове. Ищите незакрытую конструкцию выше. - ❌ Пропустили
ТОГДАили поставили запятую между ветками. Ветки разделяются только словом КОГДА, запятые между ними не нужны. - ❌ Написали
?()илиЕслииз встроенного языка. В запросе их нет. - ❌ Двойные кавычки внутри строки запроса во встроенном языке. Строковые константы в тексте запроса, который вы собираете в коде, пишутся удвоенными кавычками:
""Мало"".
Логические — запрос выполняется, но результат не тот
- ⚠️ Сравнение
= NULLвместоЕСТЬ NULL. Ветка просто не срабатывает. - ⚠️ Широкое условие стоит выше узкого. Узкая ветка не получает ни одной строки.
- ⚠️ Нет ИНАЧЕ. В числовой колонке появляются NULL, а итоги и последующая арифметика дают NULL — любое действие с NULL возвращает NULL.
- ⚠️ Разные типы в ветках. Если одна ветка возвращает число, а другая строку, колонка получит составной тип. Дальше это ломает сортировку и сравнения.
Ошибки группировки
Поле с ВЫБОР в запросе с СГРУППИРОВАТЬ ПО должно либо целиком стоять внутри агрегатной функции, либо повторяться в группировке тем же выражением. Иначе платформа сообщит, что поле не входит в группу. Решения два: перенести ВЫБОР внутрь СУММА(), МАКСИМУМ() и других агрегатных функций, или скопировать выражение в СГРУППИРОВАТЬ ПО без псевдонима.
Деление на ноль
Расчёт средней цены Сумма / Количество падает на строке с нулевым количеством. Ветка спасает:
ВЫБОР
КОГДА Итоги.Количество = 0 ТОГДА 0
ИНАЧЕ Итоги.Сумма / Итоги.Количество
КОНЕЦ КАК СредняяЦена
Строки неограниченной длины
Реквизит строки без ограничения длины (например, комментарий) нельзя сравнивать напрямую — ни в ГДЕ, ни в КОГДА. Обрежьте значение: КОГДА ПОДСТРОКА(Док.Комментарий, 1, 100) = "срочно". Для поиска по части текста ПОДОБНО с такой обрезкой тоже работает.
Как отлаживать
- Выполните запрос в консоли запросов — внешней обработке, которую используют разработчики и администраторы. Там видна каждая строка результата.
- Вынесите условие каждой ветки отдельной колонкой:
Товары.Количество < 10 КАК Проверка1. Сразу видно, какое условие истинно для конкретной строки. - Для полей составного типа добавьте колонку
ТИПЗНАЧЕНИЯ(Поле)— станет понятно, почему не срабатываетССЫЛКА. - Если подозреваете NULL — добавьте колонку
Поле ЕСТЬ NULL. - Когда ветки работают — уберите отладочные колонки.
В конструкторе запроса произвольное выражение вводится в редакторе поля. Длинные ВЫБОР удобнее править прямо в тексте запроса, а конструктор использовать для таблиц и соединений.
Оптимизация: как ВЫБОР влияет на скорость запроса
В списке полей ВЫБОР почти ничего не стоит: условие вычисляется над строками, которые и так уже выбраны. Проблемы начинаются в трёх местах.
ВЫБОР в ГДЕ и в условии соединения
Условие вида ГДЕ ВЫБОР КОГДА … КОНЕЦ = 1 СУБД не может разложить на индекс. Она вычисляет выражение для каждой строки таблицы — то есть читает её целиком. На регистре в миллионы записей это заметно.
Что делать:
- ✅ Переписать условие обычной логикой:
ГДЕ (А И Б) ИЛИ (НЕ А И В)вместо ВЫБОР. - ✅ Если ИЛИ всё равно мешает индексу — разбить запрос на две части через
ОБЪЕДИНИТЬ ВСЕ, у каждой своё простое условие. - ✅ Отбор по измерениям регистра переносить в параметры виртуальной таблицы —
Остатки(&Дата, Склад = &Склад). Там он применяется до расчёта.
Обращение через точку к полю составного типа
ВЫБОР КОГДА Движения.Регистратор.Контрагент = &Контрагент … выглядит невинно. Но если регистратор может быть документом десяти типов, платформа добавит в запрос соединения со всеми десятью таблицами. Уточните тип заранее:
ВЫБОР
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент
ИНАЧЕ НЕОПРЕДЕЛЕНО
КОНЕЦ КАК Контрагент
С ВЫРАЗИТЬ соединение будет одно — с таблицей нужного документа.
Повторение одного и того же ВЫБОР
Если одно и то же длинное выражение нужно в полях, в группировке и в сортировке, вычислите его один раз во временной таблице. Следующий запрос пакета обращается к готовой колонке:
ВЫБРАТЬ
Продажи.Контрагент,
ВЫБОР КОГДА Продажи.Сумма >= 1000000 ТОГДА "Крупный" ИНАЧЕ "Обычный" КОНЕЦ КАК Сегмент,
Продажи.Сумма
ПОМЕСТИТЬ ВТ_Сегменты
ИЗ
&ТаблицаПродаж КАК Продажи
;
ВЫБРАТЬ
ВТ.Сегмент,
СУММА(ВТ.Сумма) КАК Сумма
ИЗ
ВТ_Сегменты КАК ВТ
СГРУППИРОВАТЬ ПО
ВТ.Сегмент
Здесь &ТаблицаПродаж — таблица значений, переданная параметром. Подставьте свой источник. Текст второго запроса становится короче, а ошибка в одном месте не расходится по трём.
Заменять пять обычных запросов одним с СУММА(ВЫБОР …) выгодно, когда источник один. Если ради одного запроса вы соединяете несвязанные таблицы, выигрыш пропадает: проще оставить отдельные запросы в пакете.
Частые вопросы
Можно ли использовать ВЫБОР в УПОРЯДОЧИТЬ ПО?
УПОРЯДОЧИТЬ ПО ВЫБОР КОГДА Срок < &Дата ТОГДА 0 ИНАЧЕ 1 КОНЕЦ, Срок.Какой тип будет у колонки, если ветки возвращают строки разной длины?
Сколько веток КОГДА можно написать?
Работает ли ВЫБОР в вычисляемых полях СКД?
Выбор Когда … Тогда … Иначе … Конец. Она вычисляется над результатом запроса, а не над таблицами базы.Почему ВЫБОР возвращает NULL, хотя ИНАЧЕ указано?
ЕСТЬNULL().