При разработке на платформе 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С, СУБД сама решает, в каком порядке проверять условия в ГДЕ.ВЫРАЗИТЬ. Это самый надежный способ работы с полями составного типа (такими как значения характеристик или дополнительных реквизитов).ВЫБОР безопаснее, чем простые логические операторы, когда нужно жестко разграничить условия, но она не является 100% защитой от всех типов ошибок выполнения в SQL.ЛОЖЬ.Соблюдение этих правил позволит вам писать отказоустойчивые запросы, которые будут корректно работать на любых СУБД и при любых объемах данных.