Как написать ВЫБОР с несколькими условиями в запросе 1С 8.3
Содержание
В 1С ВЫБОР КОГДА с условием И пишется так: ВЫБОР КОГДА Условие1 И Условие2 ТОГДА Значение ИНАЧЕ Другое КОНЕЦ. Ветка срабатывает, только если истинны обе части. Условия веток проверяются сверху вниз, первая истинная ветка даёт результат, остальные не смотрятся.
Оператор ВЫБОР есть только в языке запросов и в выражениях системы компоновки данных (СКД). Во встроенном языке его нет: там используют Если … ИначеЕсли … КонецЕсли или тернарный оператор ?(Условие, Значение1, Значение2). Всё, что ниже, работает в платформе 8.3 в любой конфигурации: БП 3.0, ЗУП 3.1, УТ 11 и самописных. Имена таблиц и реквизитов в примерах условные — подставьте свои.
Синтаксис ВЫБОР КОГДА и как платформа проверяет условия
Полная форма оператора:
ВЫБОР
КОГДА <логическое выражение 1> ТОГДА <значение 1>
КОГДА <логическое выражение 2> ТОГДА <значение 2>
...
ИНАЧЕ <значение по умолчанию>
КОНЕЦ
Что нужно помнить о каждой части:
- 🔹
ВЫБОРоткрывает конструкцию,КОНЕЦзакрывает. БезКОНЕЦзапрос не разберётся. - 🔹
КОГДА— логическое выражение. Можно сравнения,И,ИЛИ,НЕ,В,МЕЖДУ,ПОДОБНО,ЕСТЬ NULL,ССЫЛКА. - 🔹
ТОГДА— что вернуть: поле, число, строку, параметр, другой ВЫБОР. - 🔹
ИНАЧЕнеобязателен. Но если его нет и ни одна ветка не подошла, результат —NULL.
Порядок веток имеет значение. Платформа идёт сверху вниз и останавливается на первой истинной ветке. Поэтому узкие условия ставят выше широких.
ВЫБОР
КОГДА Продажи.Сумма >= 1000000 ТОГДА "Крупный"
КОГДА Продажи.Сумма >= 100000 ТОГДА "Средний"
ИНАЧЕ "Мелкий"
КОНЕЦ КАК Сегмент
Если поменять первые две строки местами, сумма 1 500 000 попадёт в «Средний»: условие >= 100000 для неё тоже истинно и стоит раньше.
ВЫБОР можно ставить почти в любую часть запроса: в список полей ВЫБРАТЬ, в ГДЕ, внутрь агрегатных функций (СУММА, КОЛИЧЕСТВО), в УПОРЯДОЧИТЬ ПО, в условие соединения. Это обычное выражение, которое возвращает значение.
Условие И в ветке КОГДА
Связка И требует, чтобы все части были истинны:
ВЫБОР
КОГДА Документ.Проведен И Документ.СуммаДокумента > 0
ТОГДА "В работе"
ИНАЧЕ "Пропустить"
КОНЕЦ КАК Статус
Частей может быть сколько угодно: КОГДА А И Б И В. Читается проще, если каждую часть перенести на новую строку и выровнять.
Логическое значение как результат
Когда нужно поле-флаг «да/нет», пишут ветки с ИСТИНА и ЛОЖЬ:
ВЫБОР
КОГДА Остатки.КоличествоОстаток < 0 ТОГДА ИСТИНА
ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК Минус
Такое поле удобно потом использовать в отборе СКД или в условном оформлении.
Приоритет И над ИЛИ: где без скобок ВЫБОР ошибается
В языке запросов 1С, как и в SQL, порядок такой: сначала НЕ, затем И, затем ИЛИ. Это главная причина «неправильных» результатов в ветках с несколькими условиями.
Пример. Нужно пометить продажи одному из двух складов на сумму больше 50 000:
// Неверно
КОГДА Склад = &СкладА ИЛИ Склад = &СкладБ И Сумма > 50000 ТОГДА "Проверить"
Платформа прочитает это как Склад = &СкладА ИЛИ (Склад = &СкладБ И Сумма > 50000). В итоге все продажи склада А попадут в «Проверить», даже на 100 рублей.
// Верно
КОГДА (Склад = &СкладА ИЛИ Склад = &СкладБ) И Сумма > 50000 ТОГДА "Проверить"
Ещё короче — через В:
КОГДА Склад В (&СкладА, &СкладБ) И Сумма > 50000 ТОГДА "Проверить"
| Запись | Как читает платформа | Когда истинно |
|---|---|---|
А ИЛИ Б И В | А ИЛИ (Б И В) | А истинно — сразу ветка |
(А ИЛИ Б) И В | как написано | В обязательно |
НЕ А И Б | (НЕ А) И Б | А ложно и Б истинно |
НЕ (А И Б) | как написано | хотя бы одно ложно |
Правило простое: смешали И с ИЛИ в одной ветке — ставьте скобки, даже если уверены в приоритете. Через полгода этот запрос будет читать другой человек, и скобки избавят его от гаданий.
Когда лучше разбить на несколько КОГДА
Длинное условие с пятью ИЛИ часто удобнее развести по веткам с одинаковым результатом:
ВЫБОР
КОГДА Сумма > 50000 И Склад = &СкладА ТОГДА "Проверить"
КОГДА Сумма > 50000 И Склад = &СкладБ ТОГДА "Проверить"
КОГДА Контрагент.НеРезидент ТОГДА "Проверить"
ИНАЧЕ "ОК"
КОНЕЦ
Каждую ветку легко закомментировать при отладке. Результат тот же, что у одной ветки с ИЛИ и скобками.
NULL и пустые значения в условиях ВЫБОР
NULL появляется в запросе чаще, чем кажется:
- 🔸 после
ЛЕВОЕ СОЕДИНЕНИЕ, когда справа нет пары; - 🔸 при обращении через точку к реквизиту составного типа, если значение другого типа;
- 🔸 в ветке ВЫБОР без
ИНАЧЕ; - 🔸 в итогах виртуальных таблиц, если движений не было.
Любое сравнение с NULL не даёт истину. Поэтому КОГДА Цена > 0 и КОГДА НЕ Цена > 0 при NULL обе не сработают, и запрос уйдёт в ИНАЧЕ. Это главная ловушка при условии с НЕ.
Проверять NULL нужно только так:
КОГДА Цены.Цена ЕСТЬ NULL ТОГДА 0
Запись Цены.Цена = NULL — ошибка: истиной она не станет никогда.
Для замены NULL на значение есть короткая функция ЕСТЬNULL. Две записи ниже равнозначны:
ВЫБОР КОГДА Цены.Цена ЕСТЬ NULL ТОГДА 0 ИНАЧЕ Цены.Цена КОНЕЦ
ЕСТЬNULL(Цены.Цена, 0)
NULL, пустая ссылка, пустая строка — это разное
| Что проверяем | Как записать условие |
|---|---|
| нет записи после соединения | Поле ЕСТЬ NULL |
| ссылка не заполнена | Поле = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка) |
| строка не заполнена | Поле = "" |
| дата не заполнена | Поле = ДАТАВРЕМЯ(1, 1, 1) |
| число не заполнено | Поле = 0 |
Если поле может быть и NULL, и пустой ссылкой, проверяйте оба случая в одной ветке:
КОГДА Партнер.Ссылка ЕСТЬ NULL
ИЛИ Партнер.Ссылка = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
ТОГДА "Не указан"
Здесь ИЛИ один, без И, поэтому скобки не нужны.
Защита от деления на ноль
Цена за единицу в отчёте — частый случай, где ВЫБОР спасает запрос:
ВЫБОР
КОГДА Остатки.КоличествоОстаток = 0 ТОГДА 0
ИНАЧЕ Остатки.СуммаОстаток / Остатки.КоличествоОстаток
КОНЕЦ КАК СредняяЦена
Если количество может быть ещё и NULL, оберните его: ЕСТЬNULL(Остатки.КоличествоОстаток, 0) = 0.
Тип результата и сравнение ссылок
Ветки ВЫБОР могут возвращать разные типы. Тогда поле результата получается составного типа. Для отчёта это обычно не проблема, но в соединениях и группировках такие поля ведут себя хуже.
Привести результат к одному типу помогает ВЫРАЗИТЬ:
ВЫРАЗИТЬ(
ВЫБОР
КОГДА Док.Комментарий = "" ТОГДА "Без комментария"
ИНАЧЕ Док.Комментарий
КОНЕЦ КАК СТРОКА(100)) КАК Примечание
Ссылки сравнивают через ЗНАЧЕНИЕ() или параметр, а не со строкой:
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход) ТОГДА 1
Для полей составного типа, например Регистратор, тип проверяют оператором ССЫЛКА:
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг ТОГДА "Продажа"
Производительность запросов с ВЫБОР
Сам по себе ВЫБОР в списке полей почти ничего не стоит: он считается для строк, которые уже отобраны. Проблемы начинаются, когда его ставят туда, где СУБД нужно искать данные.
ВЫБОР в ГДЕ мешает индексам
Частый приём для «необязательного отбора»:
ГДЕ
ВЫБОР
КОГДА &ОтборПоКонтрагенту ТОГДА Продажи.Контрагент = &Контрагент
ИНАЧЕ ИСТИНА
КОНЕЦ
Работает, но СУБД не может использовать индекс по Контрагент: условие спрятано внутри выражения. На таблице в миллион строк это полный просмотр.
Как лучше:
- Собирать текст запроса во встроенном языке. Если отбор не нужен — условия в тексте просто нет.
- Для виртуальных таблиц регистров отбор передавать в параметры таблицы, а не в
ГДЕ. - Если запрос один на все случаи, хотя бы писать
(НЕ &ОтборПоКонтрагенту ИЛИ Продажи.Контрагент = &Контрагент)— его проще читать и отлаживать.
ТекстОтбора = "";
Если ЗначениеЗаполнено(Контрагент) Тогда
ТекстОтбора = " И Продажи.Контрагент = &Контрагент";
КонецЕсли;
Запрос.Текст = ТекстЗапроса + ТекстОтбора;
Запрос.УстановитьПараметр("Контрагент", Контрагент);
Здесь ТекстЗапроса должен заканчиваться условием ГДЕ ИСТИНА или другим условием, к которому можно дописать И.
Разыменование внутри веток
Каждое обращение через точку (Контрагент.ИНН, Регистратор.Склад) — это неявное соединение с другой таблицей. Если в трёх ветках ВЫБОР обращаться к реквизитам Регистратор, а он ссылается на 40 видов документов, запрос соединится со всеми сорока таблицами.
Решение — указать тип явно:
КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
ТОГДА ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Склад
Тяжёлые выражения ВЫБОР в СГРУППИРОВАТЬ ПО и условиях соединения лучше вынести во временную таблицу: сначала посчитать поле, проиндексировать, затем группировать или соединять.
Частые ошибки в ВЫБОР КОГДА и как их исправить
Ещё две ошибки, которые не дают сообщения, а просто искажают цифры:
- ❌ Широкое условие выше узкого. Ветки проверяются по порядку, нижняя никогда не сработает. Сортируйте ветки от самого строгого условия к общему.
- ❌ Попытка использовать ВЫБОР в коде модуля. Во встроенном языке такой конструкции нет, будет ошибка компиляции. Пишите
?()илиЕсли.
Примеры ВЫБОР с условием И для отчётов
Приход и расход в одном запросе
ВЫБРАТЬ
Движения.Номенклатура КАК Номенклатура,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Приход,
СУММА(ВЫБОР
КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
ТОГДА Движения.Количество
ИНАЧЕ 0
КОНЕЦ) КАК Расход
ИЗ
РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
Движения.Период МЕЖДУ &НачалоПериода И &КонецПериода
СГРУППИРОВАТЬ ПО
Движения.Номенклатура
ИНАЧЕ 0 здесь обязателен. Без него сумма по строкам без прихода вернёт NULL, и колонка будет пустой.
Сегментация клиентов по двум признакам
Нужно выделить крупных клиентов с просрочкой. Два условия через И:
ВЫБОР
КОГДА Долги.Сумма > 500000 И Долги.ДнейПросрочки > 30
ТОГДА "Крупный, просрочка"
КОГДА Долги.Сумма > 500000
ТОГДА "Крупный"
КОГДА Долги.ДнейПросрочки > 30
ТОГДА "Просрочка"
ИНАЧЕ "Норма"
КОНЕЦ КАК Группа
Первой стоит ветка с обоими условиями — она самая строгая. Если поставить её третьей, клиент с долгом 600 000 и просрочкой 45 дней попадёт в «Крупный».
Проверка на числах:
| Сумма | Дней просрочки | Группа |
|---|---|---|
| 600 000 | 45 | Крупный, просрочка |
| 600 000 | 10 | Крупный |
| 80 000 | 45 | Просрочка |
| 80 000 | 10 | Норма |
Свой порядок сортировки
Иногда группы нужно показать не по алфавиту, а в рабочем порядке:
УПОРЯДОЧИТЬ ПО
ВЫБОР
КОГДА Заказы.Статус = &СтатусНовый ТОГДА 1
КОГДА Заказы.Статус = &СтатусВРаботе ТОГДА 2
ИНАЧЕ 3
КОНЕЦ,
Заказы.Дата
Вычисляемое поле в СКД
В схеме компоновки данных то же выражение пишут на вкладке вычисляемых полей. Язык выражений СКД понимает конструкцию Выбор Когда … Тогда … Иначе … Конец и те же операторы И, ИЛИ, НЕ. Скобки и правило приоритета работают так же, как в запросе.
Разница в том, где выполняется расчёт. Поле в тексте запроса считает СУБД. Вычисляемое поле СКД считается уже на сервере 1С после получения данных. Для больших выборок выгоднее держать ВЫБОР в запросе.