Будет ли проверяться второе условие в запросе 1С, если первое не выполняется?

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

При разработке на платформе 1С:Предприятие программисты часто проводят параллели между логикой встроенного языка и логикой запросов. Во встроенном языке выражение Если Условие1 И Условие2 Тогда работает по принципу «ленивых вычислений» (short-circuit evaluation): если Условие1 ложно, то Условие2 даже не будет проверяться. Однако в языке запросов ситуация обстоит иначе. Разберем подробно, как ведет себя СУБД при обработке условий и как избежать ошибок при работе с потенциально некорректными данными.

Порядок вычисления условий в секции ГДЕ

Рассмотрим ситуацию, когда в запросе нам необходимо наложить два фильтра, причем второй фильтр может вызвать ошибку (например, несоответствие типов или деление на ноль), если первый фильтр не отсечет ненужные записи. Проанализируем стандартную конструкцию:


ВЫБРАТЬ
    Реализация.Ссылка
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
    Реализация.Дата > &ДатаНачала
    И Реализация.Номер = &НужныйНомер

Многие полагают, что условия в секции ГДЕ проверяются строго сверху вниз. На самом деле, запрос в 1С транслируется в SQL, и окончательное решение о порядке проверки условий принимает оптимизатор СУБД (MS SQL Server, PostgreSQL, IBM DB2 или Oracle). Оптимизатор руководствуется статистикой распределения данных и индексами. Если он посчитает, что второе условие более «селективно» (быстрее отсечет большую часть строк), он может начать проверку именно с него.

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

Особенности работы с Дополнительными реквизитами

Часто вопрос о последовательности проверки возникает при работе с табличными частями дополнительных реквизитов. Рассмотрим пример, когда нам нужно найти документы, у которых определенное свойство имеет значение Истина (Булево). Проблема в том, что поле Значение в дополнительных реквизитах имеет составной тип. Если мы просто напишем:


ГДЕ
    ДополнительныеРеквизиты.Свойство = &СвойствоБулево
    И ДополнительныеРеквизиты.Значение = ИСТИНА

Мы рискуем столкнуться с тем, что система попытается сравнить значение типа СправочникСсылка с типом Булево для других строк, где свойство не совпадает с искомым. Хотя 1С достаточно гибко обрабатывает сравнение типов в запросах (обычно возвращая ЛОЖЬ вместо падения в ошибку), в сложных конструкциях или при использовании специфических функций СУБД это может привести к непредсказуемым результатам или снижению производительности.

Использование конструкции ВЫРАЗИТЬ для безопасной типизации

Чтобы гарантированно избежать проблем с типами данных, мы рекомендуем использовать оператор ВЫРАЗИТЬ. Этот оператор предназначен для приведения значений составного типа к конкретному типу. Проанализируем правильный подход:


ГДЕ
    ДополнительныеРеквизиты.Свойство = &СвойствоБулево
    И ВЫРАЗИТЬ(ДополнительныеРеквизиты.Значение КАК БУЛЕВО) = ИСТИНА

В чем преимущество такого метода? Если в поле Значение лежит ссылка на справочник, а мы пытаемся «выразить» его как БУЛЕВО, система вернет NULL. Сравнение NULL = ИСТИНА даст результат Ложь, и запрос отработает корректно без каких-либо ошибок типов.

Поможет ли оператор ВЫБОР КОГДА?

Существует мнение, что конструкция ВЫБОР КОГДА гарантирует последовательность вычислений. Выясним, так ли это. Рассмотрим пример:


ГДЕ
    ВЫБОР
        КОГДА ДополнительныеРеквизиты.Свойство = &СвойствоБулево
            ТОГДА ВЫРАЗИТЬ(ДополнительныеРеквизиты.Значение КАК БУЛЕВО)
        ИНАЧЕ ЛОЖЬ
    КОНЕЦ = ИСТИНА

Действительно, стандарт SQL требует, чтобы CASE возвращал результат первого истинного условия. Однако современные оптимизаторы СУБД могут выполнять предварительное вычисление скалярных выражений во всех ветках, если это не ведет к явной синтаксической ошибке. Тем не менее, использование ВЫБОР внутри запроса считается гораздо более безопасным инструментом для реализации логики ветвления, чем простое перечисление условий через И.

Рекомендации по построению условий в соединениях

Для оптимизации и повышения надежности кода разберем еще один метод — вынос условия по свойству в секцию ПО (связи таблиц). Проанализируем ситуацию:


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

В этом случае мы сначала ограничиваем выборку только нужным свойством на этапе соединения таблиц, а затем в секции ГДЕ проверяем значение. Использование ЕСТЬNULL позволяет корректно обработать строки, где доп. реквизит вообще не задан.

Итоги и выводы

Подведем итоги нашего исследования:

  1. Забудьте о «ленивых вычислениях» в запросах. В отличие от кода 1С, СУБД сама решает, в каком порядке проверять условия в ГДЕ.
  2. Используйте ВЫРАЗИТЬ. Это самый надежный способ работы с полями составного типа (такими как значения характеристик или дополнительных реквизитов).
  3. Конструкция ВЫБОР безопаснее, чем простые логические операторы, когда нужно жестко разграничить условия, но она не является 100% защитой от всех типов ошибок выполнения в SQL.
  4. Типизация в 1С. Большинство ошибок «Несоответствие типов» отлавливается еще на этапе компиляции запроса. Если запрос прошел синтаксическую проверку, то при сравнении разных типов в процессе выполнения он, скорее всего, просто вернет ЛОЖЬ.

Соблюдение этих правил позволит вам писать отказоустойчивые запросы, которые будут корректно работать на любых СУБД и при любых объемах данных.

← На главную