В практике разработки на платформе 1С программисты часто сталкиваются с необходимостью построения гибких запросов, которые должны учитывать наличие или отсутствие определенного параметра. Типичный пример — фильтрация по спецификации: если параметр заполнен, выбираем данные по нему, если нет — используем основную спецификацию. На первый взгляд, такие условия кажутся одинаковыми, однако с точки зрения производительности СУБД разница может быть существенной. Проанализируем ситуацию подробно.
Рассмотрим два варианта написания условия ГДЕ, которые предложил автор темы:
Вариант 1: Использование классической логики И/ИЛИ
&Спецификация = НЕОПРЕДЕЛЕНО
И РесурсныеСпецификацииВыходныеИзделия.Ссылка = ОсновныеСпецификации.Спецификация
ИЛИ &Спецификация = РесурсныеСпецификацииВыходныеИзделия.Ссылка
Вариант 2: Использование конструкции ВЫБОР КОГДА
ВЫБОР
КОГДА &Спецификация = НЕОПРЕДЕЛЕНО
ТОГДА РесурсныеСпецификацииВыходныеИзделия.Ссылка = ОсновныеСпецификации.Спецификация
ИНАЧЕ &Спецификация = РесурсныеСпецификацииВыходныеИзделия.Ссылка
КОНЕЦ
С точки зрения формальной логики эти конструкции эквивалентны. В обоих случаях, если параметр &Спецификация не задан, сработает первое условие сравнения ссылок. Если параметр задан — второе. Однако то, как эти условия будут преобразованы в SQL-код и как их обработает оптимизатор базы данных, — ключевой вопрос производительности.
Основная причина медленной работы подобных запросов на больших таблицах заключается в потере SARGability (Search Argumentable). СУБД эффективно работает тогда, когда может применить Index Seek (поиск по индексу). Когда мы объединяем условия через ИЛИ или используем CASE (в который транслируется ВЫБОР) внутри блока WHERE, оптимизатор SQL Server часто не может заранее определить, какую часть индекса использовать.
Проанализируем, что происходит при выполнении:
Index Scan (полное сканирование индекса) или даже Table Scan, так как логика условия зависит от значения внешнего параметра, который может меняться.WHERE не гарантирован. Оптимизатор может сначала проверить вторую часть ИЛИ, если посчитает это более выгодным согласно статистике, что иногда приводит к неожиданным замедлениям.Как показывает практика и опыт разработчиков крупных систем (например, ERP или УТ 11), конструкция ВЫБОР КОГДА часто ведет себя чуть стабильнее. Это связано с тем, что CASE в SQL более жестко определяет логическую ветку. Однако на действительно больших объемах данных (миллионы строк) оба варианта могут «тормозить», если оптимизатор решит, что универсальный план лучше, чем риск использования неподходящего индекса.
Выясним причину, почему это считается «золотым стандартом» оптимизации. Вместо того чтобы заставлять СУБД гадать, какое условие применить, мы можем передать ей уже готовый, максимально простой запрос. Разберем по шагам, как это реализовать в коде 1С:
ТекстУсловия = "";
Если ЗначениеЗаполнено(ПараметрСпецификация) Тогда
ТекстУсловия = "ГДЕ РесурсныеСпецификацииВыходныеИзделия.Ссылка = &Спецификация";
Иначе
ТекстУсловия = "ГДЕ РесурсныеСпецификацииВыходныеИзделия.Ссылка = ОсновныеСпецификации.Спецификация";
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ИЗ ... " + ТекстУсловия;
Запрос.УстановитьПараметр("Спецификация", ПараметрСпецификация);
В этом случае СУБД получит два абсолютно разных запроса. Для каждого из них будет построен свой идеальный план выполнения, использующий Index Seek по полю Ссылка или по связке со спецификацией. Это гарантирует максимальную скорость вне зависимости от объема данных.
Если по каким-то причинам (например, в динамическом списке) мы не можем менять текст запроса программно, рекомендуется использовать ОБЪЕДИНИТЬ ВСЕ. Рассмотрим пример структуры такого запроса:
ВЫБРАТЬ ...
ГДЕ &Спецификация <> НЕОПРЕДЕЛЕНО
И РесурсныеСпецификацииВыходныеИзделия.Ссылка = &Спецификация
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ ...
ГДЕ &Спецификация = НЕОПРЕДЕЛЕНО
И РесурсныеСпецификацииВыходныеИзделия.Ссылка = ОсновныеСпецификации.Спецификация
Несмотря на то что кода становится больше, для SQL Server это выглядит как два независимых простых поиска. Оптимизатор легко отрабатывает каждую часть, и общая скорость выполнения будет значительно выше, чем при использовании ИЛИ.
Подведем итоги нашего анализа:
ИЛИ и ВЫБОР КОГДА логически идентичны.ВЫБОР КОГДА может работать чуть быстрее в некоторых случаях, но оба метода проигрывают чистым условиям.ОБЪЕДИНИТЬ ВСЕ для разделения логических веток.Использование правильных конструкций в Запрос — это залог масштабируемости вашей системы. Даже если сейчас данных мало и разница незаметна, при росте базы неправильное условие ГДЕ может стать критической точкой отказа.