Оператор ИЛИ во встроенном языке и запросах 1С:Предприятия 8.3
Содержание
В 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. Если вам нужны две-три таблицы, сузьте тип явно:
ВЫРАЗИТЬ(Хозрасчетный.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент = &Контрагент
ИЛИ ВЫРАЗИТЬ(Хозрасчетный.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Контрагент = &Контрагент
Во встроенном языке тип проверяют до обращения к реквизиту, опираясь на сокращённое вычисление:
ТипДок = ТипЗнч(Регистратор);
Если ТипДок = Тип("ДокументСсылка.РеализацияТоваровУслуг")
ИЛИ ТипДок = Тип("ДокументСсылка.ПоступлениеТоваровУслуг") Тогда
Контрагент = Регистратор.Контрагент;
КонецЕсли;
Группа ИЛИ в отборах отчётов и списков
Пользователь тоже может собрать условие через ИЛИ — без кода. В настройках отчёта на системе компоновки данных (СКД) и в отборе динамического списка элементы отбора по умолчанию соединяются через И. Чтобы получить ИЛИ, условия объединяют в группу:
- Откройте настройки отчёта или списка (в списке — меню
Ещёи команда настройки списка, в отчёте — кнопкаНастройки) и перейдите на вкладку отборов. - Добавьте два или больше условий.
- Выделите их и выберите в контекстном меню группировку условий, тип группы —
Группа ИЛИ. - Сохраните настройку и сформируйте отчёт заново.
Программно то же самое делает группа элементов отбора:
Группа = Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
Группа.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИли;
Группа.Использование = Истина;
Элемент = Группа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Склад");
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Элемент.ПравоеЗначение = СкладОсновной;
Элемент.Использование = Истина;
Второй элемент добавляется в ту же группу аналогично. Для динамического списка вместо Отбор используйте Список.КомпоновщикНастроек.Настройки.Отбор или Список.Отбор. Есть и ГруппаНе — она инвертирует всё содержимое группы.
Логические ловушки с ИЛИ
Синтаксически правильный код с ИЛИ может работать совсем не так, как задумано. Вот ошибки, которые встречаются на код-ревью чаще других.
- 🔄 Неравенство через ИЛИ всегда истинно.
Если Вид <> "Товар" ИЛИ Вид <> "Услуга"пропустит любое значение: товар не равен услуге, и наоборот. НужноИ:Вид <> "Товар" И Вид <> "Услуга". - ✂️ Сокращение «как в речи».
Если Код = "01" ИЛИ "02"не проверяет второй код. Строка"02"не является логическим значением, и выражение завершится ошибкой. Каждое сравнение пишется полностью. - 🧮 Пропущенные скобки при смеси И/ИЛИ — разобрано выше. Особенно опасно в
ГДЕ, где лишние строки просто тихо попадут в отчёт. - 🕳️ Забытый NULL. Условие
Сумма > 0 ИЛИ Сумма < 0после левого соединения не пропустит строки без движений. Если они нужны — добавьтеИЛИ … ЕСТЬ NULL. - 🐢 ИЛИ по разным полям в большом регистре — запрос работает, но медленно. Проверяйте замером и переписывайте через объединение.
Проверить себя:
Частые вопросы
Можно ли писать OR вместо ИЛИ?
OR, AND, NOT. Смешивать языки в одном модуле можно, но в типовых конфигурациях принято писать по-русски.Работает ли сокращённое вычисление ИЛИ в запросе?
ГДЕ проверится первой, нельзя.Чем ИЛИ отличается от оператора В в запросе?
Поле В (1, 2, 3) — это три сравнения через ИЛИ по одному полю. Запись короче, параметр можно передать массивом, а СУБД с ней обычно работает не хуже, чем с цепочкой ИЛИ.Есть ли в 1С оператор ||, как в других языках?
|| и && в языке 1С нет, только слова ИЛИ/OR и И/AND.Как объединить ИЛИ условия по разным полям в отборе динамического списка?
ГруппаИли и положите оба условия внутрь неё. Условия, добавленные напрямую в отбор, всегда соединяются через И.