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