Как правильно настроить сложные условия с ИЛИ и И в запросе 1С и почему конструктор удаляет скобки?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

При написании сложных запросов в системе «1С:Предприятие 8» программисты часто сталкиваются с ситуацией, когда Конструктор запросов ведет себя самовольно: удаляет расставленные вручную скобки в блоке ГДЕ. Это вызывает опасения, что логика условия нарушится и запрос выдаст неверные данные. Особенно остро этот вопрос встает при работе с регистром ТоварыНаСкладах, когда нужно отфильтровать данные по нескольким типам документов-регистраторов, причем для одного типа документов условие простое, а для другого — уточняющее (например, по хозяйственной операции).

В этой статье мы подробно разберем, как работает логика приоритетов в языке запросов 1С, почему Конструктор считает скобки лишними и как написать условие так, чтобы оно работало быстро и безошибочно.

Разбор приоритетов логических операций

Для начала проанализируем ситуацию, с которой столкнулся автор темы. Ему необходимо выбрать из регистра документы двух видов:

  1. Все документы ПоступлениеТоваровУслуг.
  2. Только те документы ПриходныйОрдерНаТовары, у которых установлена хозяйственная операция Закупка.

Автор составил следующее условие:


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

После сохранения Конструктор удаляет скобки, превращая выражение в плоский список условий, соединенных через ИЛИ и И. Почему это происходит? В языке запросов 1С, как и в математике, существует жесткая иерархия (приоритет) выполнения операций. Рассмотрим ее подробнее:

  1. Выражения внутри скобок (самый высокий приоритет).
  2. Логическое НЕ.
  3. Операции сравнения (=, <>, <, >, ССЫЛКА).
  4. Логическое И (конъюнкция — логическое умножение).
  5. Логическое ИЛИ (дизъюнкция — логическое сложение).

Поскольку приоритет И всегда выше, чем приоритет ИЛИ, выражение А ИЛИ Б И В всегда будет вычисляться как А ИЛИ (Б И В). Конструктор запросов видит, что ваши скобки не меняют стандартный порядок вычислений, и удаляет их как избыточные. Это не ошибка системы, а синтаксическая нормализация кода.

Как система вычисляет результат условия

Давайте проанализируем, как отработает условие без скобок, если в запрос попадет документ ПоступлениеТоваровУслуг:

  1. Проверяется первая часть: Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг. Результат — ИСТИНА.
  2. Далее идет оператор ИЛИ. Согласно правилам логики, если первая часть выражения ИЛИ равна ИСТИНА, то все остальное выражение уже не может изменить итоговый результат на ЛОЖЬ (в контексте SQL-подобных языков результат будет ИСТИНА).
  3. Даже если вторая часть (про Приходный ордер) вернет ЛОЖЬ, общее выражение останется истинным: ИСТИНА ИЛИ ЛОЖЬ = ИСТИНА.

Рассмотрим обратную ситуацию, когда в запрос попал ПриходныйОрдерНаТовары с операцией «Возврат»:

  1. Первая проверка (на Поступление) дает ЛОЖЬ.
  2. Вторая проверка (на Приходный ордер) дает ИСТИНА.
  3. Третья проверка (на Хозяйственную операцию) дает ЛОЖЬ.
  4. Вычисляется блок И: ИСТИНА И ЛОЖЬ = ЛОЖЬ.
  5. Вычисляется общий итог: ЛОЖЬ ИЛИ ЛОЖЬ = ЛОЖЬ. Запись не попадет в выборку, что и требовалось.

Ловушка производительности: Неявные соединения

Хотя логически условие автора верно, с точки зрения производительности оно содержит серьезную проблему. Когда мы обращаемся к реквизиту через точку от поля составного типа (Регистратор.ХозяйственнаяОперация) или используем ВЫРАЗИТЬ в блоке ГДЕ, СУБД вынуждена выполнять неявные левые соединения (LEFT JOIN) со всеми таблицами документов, которые могут быть регистраторами данного регистра.

Если в вашей конфигурации у регистра ТоварыНаСкладах десятки регистраторов, запрос будет «тяжелым» и медленным, даже если фактически данных в базе мало. СУБД будет сканировать и присоединять таблицы, которые нам в данном конкретном случае вообще не нужны.

Особенности работы ВЫРАЗИТЬ и NULL

Важно понимать, как работает ВЫРАЗИТЬ, когда тип документа не совпадает. Если наш Регистратор — это ПоступлениеТоваровУслуг, а мы пишем ВЫРАЗИТЬ(Регистратор КАК Документ.ПриходныйОрдерНаТовары), система вернет значение NULL. Любое сравнение NULL с конкретным значением (например, с перечислением) дает результат UNKNOWN (неизвестно). В блоке ГДЕ этот результат интерпретируется как ЛОЖЬ. Именно поэтому условие работает корректно, но требует от системы лишних вычислений.

Рекомендуемое решение: Использование ОБЪЕДИНИТЬ ВСЕ

Чтобы избежать проблем с производительностью и сделать код более читаемым, профессиональные разработчики 1С рекомендуют использовать конструкцию ОБЪЕДИНИТЬ ВСЕ. Вместо одного сложного запроса с кучей условий ИЛИ, мы разделяем задачу на два простых и максимально быстрых запроса.

Рассмотрим этот способ по шагам:

  1. В первом запросе выбираем данные только по документам поступления.
  2. Во втором запросе выбираем данные только по приходным ордерам с нужной операцией.
  3. Объединяем результаты.

Пример реализации:


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

ОБЪЕДИНИТЬ ВСЕ

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

Преимущества такого подхода:

Подведем итоги

Мы выяснили, что поведение Конструктора запросов, удаляющего скобки, является нормальным и обусловлено приоритетами логических операций. Оператор И всегда выполняется перед ИЛИ. Однако для построения по-настоящему эффективных систем следует избегать сложных условий в блоке ГДЕ при работе с составными типами данных.

Используйте метод разделения запросов через ОБЪЕДИНИТЬ ВСЕ — это позволит вам писать надежный код, который будет работать быстро даже на очень больших объемах данных. И помните: если голова «не работает» от нагромождения условий — это верный признак того, что запрос пора упрощать или разделять на части.

← На главную