Условия в запросе 1С через ВЫБОР КОГДА ТОГДА: от простой ветки до каскада

· 10 мин

Содержание

Условие в запросе 1С пишется конструкцией ВЫБОР КОГДА <условие> ТОГДА <значение> ИНАЧЕ <значение> КОНЕЦ. Это аналог CASE WHEN ... THEN ... ELSE ... END из SQL: платформа проверяет ветки сверху вниз и возвращает значение первой, где условие истинно. Нет подходящей ветки и нет ИНАЧЕ — результат будет NULL.

Функцию КОГДА в языке запросов 1С используют, чтобы раскрасить поле статусом, посчитать сумму только по части строк, подменить пустое значение или отсортировать список в своём порядке. Работает одинаково в БП, ЗУП, УТ и любой другой конфигурации на платформе 8.3: это часть языка запросов, а не прикладного решения.

Синтаксис ВЫБОР КОГДА и где его можно писать

Полная форма конструкции:

ВЫБОР
    КОГДА <Условие1> ТОГДА <Выражение1>
    КОГДА <Условие2> ТОГДА <Выражение2>
    ...
    ИНАЧЕ <ВыражениеПоУмолчанию>
КОНЕЦ

Что здесь обязательно:

  • 🔹 слово ВЫБОР в начале и КОНЕЦ в конце;
  • 🔹 хотя бы одна пара КОГДА ... ТОГДА;
  • 🔸 ИНАЧЕ — необязательно, но без него непойманные строки получат NULL.

Английский вариант тоже работает: CASE WHEN ... THEN ... ELSE ... END. Смешивать русские и английские ключевые слова в одном запросе платформа позволяет, но читать такое тяжело.

Конструкция — это выражение, а не оператор. Значит, её можно поставить везде, где запрос ждёт выражение:

Место в запросеПример задачи
ВЫБРАТЬвычисляемое поле «Статус», «Знак суммы»
ГДЕусловие, которое меняется от вида строки
СГРУППИРОВАТЬ ПОгруппировка по вычисленной категории
УПОРЯДОЧИТЬ ПОсвой порядок сортировки
внутри СУММА, КОЛИЧЕСТВО, МАКСИМУМусловная агрегация
условие соединения ПОсвязь по разным полям в зависимости от типа

Минимальный пример: подпишем вид движения регистра накопления понятным словом.

ВЫБРАТЬ
    Движения.Номенклатура,
    Движения.Количество,
    ВЫБОР
        КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
            ТОГДА "Приход"
        ИНАЧЕ "Расход"
    КОНЕЦ КАК Направление
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения

Псевдоним через КАК после КОНЕЦ ставьте всегда. Без него поле получит служебное имя, и обращаться к нему из кода или из схемы компоновки будет неудобно.

Что ставить после КОГДА: сравнение со значением и логическое выражение

После КОГДА всегда стоит логическое выражение. В SQL есть короткая форма CASE Поле WHEN 1 THEN ..., где сравнение подразумевается. В языке запросов 1С пишите сравнение явно в каждой ветке: КОГДА Поле = 1 ТОГДА.

На практике условия делятся на два вида.

Сравнение с конкретным значением

Поле сравнивается с литералом, параметром или предопределённым значением:

  • 🔢 число или строка: КОГДА Товары.СтавкаНДС = 20, КОГДА Клиенты.Код = "000123";
  • 📌 параметр: КОГДА Док.Организация = &Организация;
  • 📚 перечисление или предопределённый элемент: КОГДА Док.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.Закрыт);
  • ⬜ пустая ссылка: КОГДА Док.Контрагент = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка).

Функция ЗНАЧЕНИЕ() принимает только предопределённые значения, перечисления, пустые ссылки и виды движений. Обычный элемент справочника через неё не передать — для него нужен параметр.

Произвольное логическое выражение

Здесь допустимо всё, что пишут в ГДЕ:

  • ⚖️ диапазоны: КОГДА Остатки.Количество МЕЖДУ 1 И 10;
  • 📋 списки: КОГДА Товары.Склад В (&ОсновныеСклады);
  • 🧩 комбинации: КОГДА Док.Сумма > 100000 И НЕ Док.Проведен;
  • 🕳 проверка на NULL: КОГДА Остатки.Количество ЕСТЬ NULL;
  • 🏷 проверка типа: КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг;
  • 🔤 шаблон строки: КОГДА Номенклатура.Наименование ПОДОБНО "%кабель%".

Пример классификации остатков по уровню:

ВЫБОР
    КОГДА Остатки.КоличествоОстаток <= 0 ТОГДА "Нет в наличии"
    КОГДА Остатки.КоличествоОстаток < 10 ТОГДА "Заканчивается"
    ИНАЧЕ "В наличии"
КОНЕЦ КАК УровеньЗапаса

Порядок веток здесь решает. Остаток −2 попадает в первую ветку и дальше не проверяется. Если поменять первые две ветки местами, отрицательный остаток станет «Заканчивается».

Вложенные ВЫБОР и каскад условий

Внутри ТОГДА и ИНАЧЕ может стоять ещё один ВЫБОР. Глубину вложенности платформа не ограничивает на практике, но уже третий уровень читается плохо.

Пример: скидка зависит от вида клиента, а внутри вида — от суммы заказа.

ВЫБОР
    КОГДА Заказ.Контрагент.ВидКлиента = ЗНАЧЕНИЕ(Перечисление.ВидыКлиентов.Оптовый)
        ТОГДА ВЫБОР
                КОГДА Заказ.СуммаДокумента >= 500000 ТОГДА 10
                КОГДА Заказ.СуммаДокумента >= 100000 ТОГДА 7
                ИНАЧЕ 5
              КОНЕЦ
    ИНАЧЕ ВЫБОР
            КОГДА Заказ.СуммаДокумента >= 50000 ТОГДА 3
            ИНАЧЕ 0
          КОНЕЦ
КОНЕЦ КАК ПроцентСкидки

Перечисление ВидыКлиентов здесь условное — подставьте объект своей конфигурации.

Ту же логику можно развернуть в плоский каскад, объединив условия через И:

ВЫБОР
    КОГДА Опт И Сумма >= 500000 ТОГДА 10
    КОГДА Опт И Сумма >= 100000 ТОГДА 7
    КОГДА Опт ТОГДА 5
    КОГДА Сумма >= 50000 ТОГДА 3
    ИНАЧЕ 0
КОНЕЦ

Как выбрать между формами:

ФормаКогда удобнаМинус
Плоский каскаддо 5–7 веток, условия разныеобщее условие повторяется в каждой ветке
Вложенный ВЫБОРесть явная иерархия «сначала вид, потом сумма»много уровней отступов, легко потерять КОНЕЦ
Временная таблица с промежуточным полемлогика сложная, результат нужен в нескольких местахлишний пакет в запросе

Каждый вложенный ВЫБОР закрывается своим КОНЕЦ. Ставьте КОНЕЦ строго под соответствующим ВЫБОР — так непарное слово видно сразу.

Типы результата, NULL и приведение через ВЫРАЗИТЬ

Ветки могут возвращать значения разных типов. Платформа не запрещает этого: поле получит составной тип. Пример ВЫБОР КОГДА ... ТОГДА 1 ИНАЧЕ "нет" КОНЕЦ даст поле «Число или Строка». Дальше такое поле плохо суммируется, сравнивается и группируется — лучше держать ветки в одном типе.

NULL в условии и в результате

Три правила, которые ловят чаще всего:

  1. ❌ КОГДА Поле = NULL никогда не срабатывает. Сравнение с NULL даёт «неизвестно», и ветка пропускается. Пишите КОГДА Поле ЕСТЬ NULL.
  2. ❌ Если поле пришло из левого соединения и строки справа нет, любое сравнение этого поля — тоже «неизвестно». Строка уйдёт в ИНАЧЕ, а не в ветку с условием Поле <> 0.
  3. ❌ Без ИНАЧЕ непойманные строки дают NULL. В СУММА они просто не учтутся, но в арифметике NULL + 100 тоже даст NULL.

Для простой подмены пустого значения короче функция ЕСТЬNULL:

ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток

Это то же самое, что ВЫБОР КОГДА Остатки.КоличествоОстаток ЕСТЬ NULL ТОГДА 0 ИНАЧЕ Остатки.КоличествоОстаток КОНЕЦ, только читается в одну строку. ВЫБОР нужен, когда условие сложнее, чем «значение отсутствует».

Строки и составные ссылки

Строковые реквизиты неограниченной длины (комментарии, многострочные описания) нельзя сравнивать в запросе напрямую. Сначала приведите их к строке фиксированной длины:

ВЫБОР
    КОГДА ВЫРАЗИТЬ(Док.Комментарий КАК СТРОКА(200)) = "" ТОГДА "Без комментария"
    ИНАЧЕ "Есть комментарий"
КОНЕЦ

Поле составного типа (например, Регистратор) разбирают оператором ССЫЛКА, а нужный реквизит достают через ВЫРАЗИТЬ:

ВЫБОР
    КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
        ТОГДА ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент
    КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
        ТОГДА ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Контрагент
    ИНАЧЕ ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
КОНЕЦ КАК Контрагент

ИНАЧЕ с пустой ссылкой здесь удерживает тип поля: «Контрагенты», а не «Контрагенты или NULL».

Как ВЫБОР влияет на скорость запроса

Сама конструкция вычисляется быстро — это обычное выражение на стороне СУБД. Тормозит не ВЫБОР, а то, что внутри него и где он стоит.

ВЫБОР в ГДЕ и в соединениях

Условие вида ГДЕ ВЫБОР КОГДА ... КОНЕЦ = 1 СУБД не может разложить на поиск по индексу. На большой таблице это означает полный просмотр. Порядок действий:

  1. Попробуйте переписать условие через ИЛИ и И без ВЫБОР: ГДЕ (Вид = 1 И Сумма > 0) ИЛИ (Вид = 2 И Сумма < 0).
  2. Если условие касается виртуальной таблицы (Остатки, Обороты, СрезПоследних), перенесите отбор в её параметры. Отбор внутри параметров выполняется до расчёта таблицы.
  3. Если без ВЫБОР не обойтись, сначала отберите строки простым условием по индексированному полю во временную таблицу, а вычисление сделайте уже в ней.

Неявные соединения через точку

Каждое Док.Контрагент.ВидКлиента внутри КОГДА — это соединение с таблицей справочника. Для поля составного типа Регистратор.Дата платформа соединяет таблицы всех типов, из которых состоит поле. Десять видов документов-регистраторов — десять соединений.

💡 Что помогает:

  • 🔧 перед обращением к реквизиту составного поля приведите тип через ВЫРАЗИТЬ — соединение останется одно;
  • 🔧 повторяющееся обращение через точку вынесите во временную таблицу один раз;
  • 🔧 проверку типа делайте оператором ССЫЛКА или функцией ТИПЗНАЧЕНИЯ, а не сравнением реквизитов.

Повтор одного выражения

Если тот же ВЫБОР нужен в ВЫБРАТЬ, СГРУППИРОВАТЬ ПО и УПОРЯДОЧИТЬ ПО, его придётся написать трижды, и в группировке — точно так же, как в поле. Чище посчитать поле один раз во временной таблице:

ВЫБРАТЬ
    Продажи.Номенклатура,
    Продажи.СуммаОборот,
    ВЫБОР
        КОГДА Продажи.СуммаОборот >= 1000000 ТОГДА "A"
        КОГДА Продажи.СуммаОборот >= 200000 ТОГДА "B"
        ИНАЧЕ "C"
    КОНЕЦ КАК Группа
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Группа,
    КОЛИЧЕСТВО(ВТ.Номенклатура) КАК Позиций,
    СУММА(ВТ.СуммаОборот) КАК Выручка
ИЗ
    ВТ_Продажи КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Группа
УПОРЯДОЧИТЬ ПО
    ВТ.Группа

Имена ресурсов регистра Продажи в разных конфигурациях отличаются — сверьте их в конструкторе запроса.

Шаблоны кода с КОГДА для типовых задач

Условная сумма: приход и расход в одной строке

ВЫБРАТЬ
    Движения.Номенклатура,
    СУММА(ВЫБОР
        КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Приход)
            ТОГДА Движения.Количество
        ИНАЧЕ 0
    КОНЕЦ) КАК Пришло,
    СУММА(ВЫБОР
        КОГДА Движения.ВидДвижения = ЗНАЧЕНИЕ(ВидДвиженияНакопления.Расход)
            ТОГДА Движения.Количество
        ИНАЧЕ 0
    КОНЕЦ) КАК Ушло
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    Движения.Период МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПО
    Движения.Номенклатура

ИНАЧЕ 0 здесь обязателен по смыслу: если по товару не было ни одного прихода, без него получится NULL вместо нуля.

Подсчёт строк по условию

КОЛИЧЕСТВО не считает NULL, поэтому ветку «не подходит» просто оставляют без ИНАЧЕ:

КОЛИЧЕСТВО(ВЫБОР КОГДА Заказы.Проведен ТОГДА Заказы.Ссылка КОНЕЦ) КАК Проведено,
КОЛИЧЕСТВО(ВЫБОР КОГДА НЕ Заказы.Проведен ТОГДА Заказы.Ссылка КОНЕЦ) КАК Черновиков

Защита от деления на ноль

ВЫБОР
    КОГДА Товары.Количество = 0 ТОГДА 0
    ИНАЧЕ Товары.Сумма / Товары.Количество
КОНЕЦ КАК Цена

Свой порядок сортировки

Статусы хочется видеть не по алфавиту, а по ходу работы:

УПОРЯДОЧИТЬ ПО
    ВЫБОР
        КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.КСогласованию) ТОГДА 1
        КОГДА Заказы.Статус = ЗНАЧЕНИЕ(Перечисление.СтатусыЗаказов.ВРаботе) ТОГДА 2
        ИНАЧЕ 3
    КОНЕЦ,
    Заказы.Дата

Значения перечисления СтатусыЗаказов в примере условные — у каждой конфигурации свой набор.

Логический флаг

ВЫБОР
    КОГДА Остатки.КоличествоОстаток < Номенклатура.МинимальныйЗапас ТОГДА ИСТИНА
    ИНАЧЕ ЛОЖЬ
КОНЕЦ КАК НуженЗаказ

Реквизит МинимальныйЗапас взят для примера. Такой флаг удобно использовать в условном оформлении отчёта на СКД: выделять строки, где поле равно Истина.

Запросы с ВЫБОР сначала проверяйте в консоли запросов на копии базы или с ограничением ВЫБРАТЬ ПЕРВЫЕ 100. Ошибка в условии на рабочем регистре с миллионами строк повесит сеанс надолго.

Ошибки с ВЫБОР КОГДА и как их исправить

Диагностика: выберите симптом

Ещё две ошибки не дают сообщений, но портят данные:

  • 🧮 Разные типы в ветках. ТОГДА 1 ИНАЧЕ "—" превращает поле в составное, и в отчёте суммирование по нему перестаёт работать. Держите ветки в одном типе, а прочерк рисуйте оформлением.
  • 🔗 Обращение через точку к полю из левого соединения. Если справа строки нет, условие даёт «неизвестно» и строка молча уходит в ИНАЧЕ. Проверяйте ЕСТЬ NULL первой веткой.

Частые вопросы

Можно ли использовать ВЫБОР в запросе СКД?
Да. В тексте запроса набора данных конструкция работает как обычно. В вычисляемых полях СКД есть свой язык выражений, в нём тоже доступна запись Выбор Когда … Тогда … Иначе … Конец.
Чем ВЫБОР отличается от ЕСТЬNULL?
ЕСТЬNULL подменяет только отсутствующее значение. ВЫБОР проверяет любое условие и может вернуть разное значение для разных случаев. Для замены NULL на ноль короче ЕСТЬNULL.
Можно ли в ТОГДА вернуть сразу несколько полей?
Нет. Каждый ВЫБОР возвращает одно значение. Для нескольких полей напишите несколько конструкций с одинаковыми условиями или вынесите признак ветки в отдельное поле временной таблицы.
Как задать условие ВЫБОР в конструкторе запроса?
Добавьте вычисляемое поле на вкладке с полями и откройте редактор выражения. Текст ВЫБОР … КОНЕЦ вводится вручную целиком, вместе с псевдонимом поля.
Работает ли ВЫБОР в файловой базе так же, как в клиент-серверной?
Синтаксис и результат одинаковые. Разница бывает только в скорости: план выполнения строит конкретная СУБД, поэтому тяжёлое условие в ГДЕ на одной базе почти незаметно, а на другой даёт полный просмотр таблицы.