Оператор ИЛИ во встроенном языке и запросах 1С:Предприятия 8.3

· 10 мин

Содержание

В 1С оператор ИЛИ (англ. OR) возвращает Истина, если истинен хотя бы один операнд, и Ложь, только когда ложны оба. Работает ИЛИ слева направо и с самым низким приоритетом среди логических операций: сначала выполняется НЕ, затем И, и только потом ИЛИ. Поэтому А ИЛИ Б И В платформа читает как А ИЛИ (Б И В).

Правила одинаковы для встроенного языка, языка запросов и отборов системы компоновки данных. Разница в деталях: в коде действует сокращённое вычисление, а в запросе ИЛИ влияет на то, сможет ли СУБД взять индекс. Всё ниже относится к платформе 1С:Предприятие 8.3 и не зависит от конфигурации — БП, ЗУП, УТ или своя разработка.

Синтаксис ИЛИ и таблица истинности

Оператор ставят между двумя логическими выражениями. Регистр букв не важен: ИЛИ, Или и или — одно и то же. Английский вариант OR тоже допустим.

ЕстьДолг = СуммаДолга > 0 ИЛИ ЕстьПросрочка;

Если Документ.Проведен ИЛИ Документ.ПометкаУдаления Тогда
    Сообщить("Документ нельзя редактировать");
КонецЕсли;

Результат ИЛИ всегда имеет тип Булево. Полная таблица:

АБА ИЛИ Б
ИстинаИстинаИстина
ИстинаЛожьИстина
ЛожьИстинаИстина
ЛожьЛожьЛожь

Короче: ИЛИ ложно ровно в одном случае из четырёх. Для сравнения, И истинно тоже только в одной строке — когда истинны оба операнда.

Операнды ИЛИ должны быть типа Булево. Если в выражение попадёт Неопределено (например, незаполненная переменная), платформа остановится с ошибкой Преобразование значения к типу Булево не может быть выполнено.

Сокращённое вычисление: когда правая часть не выполняется

Встроенный язык 1С вычисляет логические выражения слева направо и останавливается, как только результат известен. Для ИЛИ это значит: если левая часть равна Истина, правая уже не вычисляется — ответ всё равно будет Истина.

Этим пользуются постоянно. Классический пример — защита от обращения к реквизиту пустой ссылки или чужого типа:

Если НЕ ЗначениеЗаполнено(Контрагент) ИЛИ Контрагент.ПометкаУдаления Тогда
    ВызватьИсключение "Укажите действующего контрагента";
КонецЕсли;

Если контрагент не заполнен, проверка Контрагент.ПометкаУдаления не запускается вовсе. Поменяйте части местами — и на пустом значении получите лишнее обращение к базе или ошибку.

Из этого следуют два практических правила:

  • ⚡ дешёвое и частое условие — первым. Сравнение переменных стоит дешевле, чем обращение к реквизиту через точку (это чтение из базы) или вызов функции;
  • 🛡️ условие-«предохранитель» — слева. Проверку типа или заполненности ставьте перед тем, что без неё упадёт;
  • ⚠️ не прячьте побочные действия в правую часть. Функция, которая что-то записывает или меняет, при Истина слева просто не вызовется — и это трудно заметить при отладке.
// Плохо: ЗаписатьВЖурнал() вызовется не всегда
Если ЕстьОшибка ИЛИ ЗаписатьВЖурнал(Текст) Тогда

// Хорошо: действие отдельно, проверка отдельно
Записано = ЗаписатьВЖурнал(Текст);
Если ЕстьОшибка ИЛИ Записано Тогда

Приоритет НЕ, И, ИЛИ и зачем нужны скобки

Когда в одном выражении смешаны разные операции, 1С выполняет их в таком порядке (сверху — раньше):

ПорядокОперации
1унарные + и -
2*, /, %
3бинарные + и -
4сравнения: =, <>, <, >, <=, >=
5НЕ
6И
7ИЛИ

Сравнения выполняются раньше логики, поэтому Сумма > 0 ИЛИ Количество > 0 скобок не требует. А вот смесь И и ИЛИ без скобок — частый источник ошибок.

Пример. Нужно показать документы организации «Альфа», которые либо не проведены, либо помечены на удаление:

// Неверно: читается как (Альфа И НеПроведен) ИЛИ Помечен
Если Организация = Альфа И НЕ Проведен ИЛИ Помечен Тогда

// Верно
Если Организация = Альфа И (НЕ Проведен ИЛИ Помечен) Тогда

В первом варианте в выборку попадут помеченные документы любых организаций. Синтаксически всё корректно, поэтому ошибку никто не подсветит.

Отрицание группы с ИЛИ

НЕ относится только к ближайшему операнду. НЕ А ИЛИ Б — это (НЕ А) ИЛИ Б. Чтобы отрицать всю группу, нужны скобки: НЕ (А ИЛИ Б).

Такое отрицание раскрывается по закону де Моргана:

  • НЕ (А ИЛИ Б) = НЕ А И НЕ Б;
  • НЕ (А И Б) = НЕ А ИЛИ НЕ Б.

Правило: если в выражении есть и И, и ИЛИ — ставьте скобки, даже когда приоритет и так даёт нужный результат. Через полгода код будет читать другой человек, и скобки сэкономят ему разбор.

ИЛИ в условиях встроенного языка

Помимо Если … Тогда, ИЛИ работает везде, где ожидается логическое значение:

  • 🔁 в цикле Пока: Пока Счетчик < 100 И (НЕ Найдено ИЛИ ПроверятьВсе) Цикл;
  • ❓ в тернарном операторе: Вид = ?(ЭтоУслуга ИЛИ ЭтоРабота, "Нематериальная", "Товар");
  • 📝 в присваивании: Доступно = ЭтоАдминистратор ИЛИ РольДоступна("ПолныеПрава");
  • 🧩 в свойствах формы: Элементы.Склад.Доступность = НЕ ЭтоУслуга ИЛИ ЕстьОстатки;

Когда цепочка ИЛИ становится слишком длинной

Сравнение одной переменной с пятью значениями через ИЛИ читается плохо:

Если Код = "01" ИЛИ Код = "02" ИЛИ Код = "05" ИЛИ Код = "07" ИЛИ Код = "12" Тогда

Удобнее собрать допустимые значения в массив и проверить вхождение:

Допустимые = СтрРазделить("01,02,05,07,12", ",");
Если Допустимые.Найти(Код) <> Неопределено Тогда
    // код из списка
КонецЕсли;

Метод Найти() массива возвращает индекс элемента или Неопределено, если значения нет. Список легко вынести в константу или общий модуль. Для проверки типа значения есть ещё более короткий вариант — ОписаниеТипов:

ПростыеТипы = Новый ОписаниеТипов("Число,Строка,Дата");
Если ПростыеТипы.СодержитТип(ТипЗнч(Значение)) Тогда

Исключающего ИЛИ нет — как обойтись

Отдельного оператора XOR в 1С нет. Для двух булевых значений его заменяет неравенство: А <> Б истинно, когда ровно одно из значений равно Истина. Запись через базовые операции длиннее: (А ИЛИ Б) И НЕ (А И Б).

ИЛИ в запросах 1С и скорость выполнения

В языке запросов ИЛИ пишется так же и подчиняется тем же приоритетам. Встречается он в секциях ГДЕ и ИМЕЮЩИЕ, в условиях соединения после ПО и внутри ВЫБОР КОГДА … ТОГДА.

ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    НЕ Номенклатура.ЭтоГруппа
    И (Номенклатура.ПометкаУдаления ИЛИ Номенклатура.Наименование ПОДОБНО "%старый%")

Скобки здесь обязательны по той же причине, что и в коде: без них условие НЕ ЭтоГруппа склеится только с первой частью.

Почему ИЛИ замедляет запрос

Запрос 1С транслируется в SQL, и дальше решает СУБД. С условием ГДЕ Поле1 = &А ИЛИ Поле2 = &Б по двум разным полям оптимизатор часто не может обойтись одним индексом и читает таблицу целиком. На справочнике в тысячу строк это незаметно, на регистре с миллионами записей — минуты.

Три рабочих приёма:

СитуацияЧем заменить ИЛИЧто учесть
одно поле, несколько значенийПоле В (&Список)параметр — массив или список значений
разные поля, большая таблицадва запроса через ОБЪЕДИНИТЬстроки, подходящие под оба условия, без дублей
ИЛИ в условии соединения ПОразбить на два соединения или временную таблицуИЛИ в ПО особенно сильно бьёт по скорости

Вариант с объединением:

ВЫБРАТЬ
    Заказ.Ссылка
ИЗ
    Документ.ЗаказКлиента КАК Заказ
ГДЕ
    Заказ.Контрагент = &Контрагент

ОБЪЕДИНИТЬ

ВЫБРАТЬ
    Заказ.Ссылка
ИЗ
    Документ.ЗаказКлиента КАК Заказ
ГДЕ
    Заказ.Ответственный = &Ответственный

ОБЪЕДИНИТЬ без ВСЕ убирает повторы. Если нужен ОБЪЕДИНИТЬ ВСЕ (он быстрее, потому что не сравнивает строки), добавьте во вторую часть И Заказ.Контрагент <> &Контрагент, иначе заказ, подходящий под оба условия, придёт дважды.

Переписывать ИЛИ стоит после замера, а не заранее. Включите замер производительности или технологический журнал и посмотрите, какой запрос действительно долгий. На маленьких таблицах ИЛИ ничем не хуже объединения и читается проще.

Документ.ЗаказКлиента здесь — имя из УТ 11. В вашей конфигурации подставьте нужный документ: приём от этого не меняется.

ИЛИ с NULL и составными типами

NULL после левого соединения

Сравнение с NULL в запросе не даёт ИСТИНА. Поэтому условие ГДЕ Остатки.Количество = 0 не найдёт товары, у которых остатков нет вовсе, — после левого соединения там NULL, а не ноль.

Правильно объединить обе ветки через ИЛИ:

ГДЕ
    Остатки.КоличествоОстаток = 0
    ИЛИ Остатки.КоличествоОстаток ЕСТЬ NULL

Короче то же самое делает ЕСТЬNULL(Остатки.КоличествоОстаток, 0) = 0. Писать = NULL нельзя: для проверки на NULL есть только конструкция ЕСТЬ NULL.

Поле составного типа: ССЫЛКА и ВЫРАЗИТЬ

У поля Регистратор в регистрах, у субконто и у многих реквизитов несколько типов. Чтобы отобрать записи нескольких видов документов, ИЛИ ставят между проверками ССЫЛКА:

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

Без скобок вокруг пары ССЫЛКА условие по периоду применится только к реализациям, а поступления придут за все годы.

Отдельная ловушка — обращение через точку к полю составного типа, например Регистратор.Контрагент. Платформа строит соединение с таблицей каждого возможного типа, а условие с ИЛИ по такому полю превращается в длинный SQL. Если вам нужны две-три таблицы, сузьте тип явно:

ВЫРАЗИТЬ(Хозрасчетный.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент = &Контрагент
ИЛИ ВЫРАЗИТЬ(Хозрасчетный.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Контрагент = &Контрагент

Во встроенном языке тип проверяют до обращения к реквизиту, опираясь на сокращённое вычисление:

ТипДок = ТипЗнч(Регистратор);
Если ТипДок = Тип("ДокументСсылка.РеализацияТоваровУслуг")
    ИЛИ ТипДок = Тип("ДокументСсылка.ПоступлениеТоваровУслуг") Тогда
    Контрагент = Регистратор.Контрагент;
КонецЕсли;

Группа ИЛИ в отборах отчётов и списков

Пользователь тоже может собрать условие через ИЛИ — без кода. В настройках отчёта на системе компоновки данных (СКД) и в отборе динамического списка элементы отбора по умолчанию соединяются через И. Чтобы получить ИЛИ, условия объединяют в группу:

  1. Откройте настройки отчёта или списка (в списке — меню Ещё и команда настройки списка, в отчёте — кнопка Настройки) и перейдите на вкладку отборов.
  2. Добавьте два или больше условий.
  3. Выделите их и выберите в контекстном меню группировку условий, тип группы — Группа ИЛИ.
  4. Сохраните настройку и сформируйте отчёт заново.

Программно то же самое делает группа элементов отбора:

Группа = Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
Группа.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
Группа.Использование = Истина;

Элемент = Группа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Склад");
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Элемент.ПравоеЗначение = СкладОсновной;
Элемент.Использование = Истина;

Второй элемент добавляется в ту же группу аналогично. Для динамического списка вместо Отбор используйте Список.КомпоновщикНастроек.Настройки.Отбор или Список.Отбор. Есть и ГруппаНе — она инвертирует всё содержимое группы.

Логические ловушки с ИЛИ

Синтаксически правильный код с ИЛИ может работать совсем не так, как задумано. Вот ошибки, которые встречаются на код-ревью чаще других.

  • 🔄 Неравенство через ИЛИ всегда истинно. Если Вид <> "Товар" ИЛИ Вид <> "Услуга" пропустит любое значение: товар не равен услуге, и наоборот. Нужно И: Вид <> "Товар" И Вид <> "Услуга".
  • ✂️ Сокращение «как в речи». Если Код = "01" ИЛИ "02" не проверяет второй код. Строка "02" не является логическим значением, и выражение завершится ошибкой. Каждое сравнение пишется полностью.
  • 🧮 Пропущенные скобки при смеси И/ИЛИ — разобрано выше. Особенно опасно в ГДЕ, где лишние строки просто тихо попадут в отчёт.
  • 🕳️ Забытый NULL. Условие Сумма > 0 ИЛИ Сумма < 0 после левого соединения не пропустит строки без движений. Если они нужны — добавьте ИЛИ … ЕСТЬ NULL.
  • 🐢 ИЛИ по разным полям в большом регистре — запрос работает, но медленно. Проверяйте замером и переписывайте через объединение.

Проверить себя:

Проверьте себя
Как платформа прочитает выражение `А ИЛИ Б И В`?
Что вернёт `Ложь ИЛИ Ложь`?
Будет ли вычислена правая часть в выражении `Истина ИЛИ Функция()`?

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

Можно ли писать OR вместо ИЛИ?
Да. Встроенный язык и язык запросов 1С принимают английские ключевые слова: OR, AND, NOT. Смешивать языки в одном модуле можно, но в типовых конфигурациях принято писать по-русски.
Работает ли сокращённое вычисление ИЛИ в запросе?
На уровне запроса порядок проверки условий выбирает СУБД, а не 1С. Рассчитывать на то, что левая часть ИЛИ в ГДЕ проверится первой, нельзя.
Чем ИЛИ отличается от оператора В в запросе?
По смыслу Поле В (1, 2, 3) — это три сравнения через ИЛИ по одному полю. Запись короче, параметр можно передать массивом, а СУБД с ней обычно работает не хуже, чем с цепочкой ИЛИ.
Есть ли в 1С оператор ||, как в других языках?
Нет. Символов || и && в языке 1С нет, только слова ИЛИ/OR и И/AND.
Как объединить ИЛИ условия по разным полям в отборе динамического списка?
Создайте группу элементов отбора с типом ГруппаИли и положите оба условия внутрь неё. Условия, добавленные напрямую в отбор, всегда соединяются через И.