Как правильно и эффективно писать сложные логические условия в секции ГДЕ запроса 1С?

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

При разработке и оптимизации запросов в платформе 1С:Предприятие разработчики часто сталкиваются с необходимостью отфильтровать данные по сложному логическому условию — например, исключить попадание документов в определенный период с возможностью полного отключения этого фильтра через параметр. Разберем, как соотносятся различные варианты записи таких условий, какие из них являются тождественными, а также как они влияют на читаемость кода и производительность СУБД.

Анализ исходного условия и вариантов преобразования

Рассмотрим исходное выражение в секции ГДЕ, которое проверяет, что дата документа не попадает в указанный месяц, либо передан разрешающий параметр:


ГДЕ
    (Док.Дата >= НАЧАЛОПЕРИОДА(&ДатаОтчета, МЕСЯЦ) 
        И Док.Дата <= КОНЕЦПЕРИОДА(&ДатаОтчета, МЕСЯЦ)) = ЛОЖЬ 
    ИЛИ &Парам

Автор предложил заменить его на конструкцию с условным оператором ВЫБОР:


ГДЕ
    ВЫБОР
        КОГДА Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&ДатаОтчета, МЕСЯЦ) И КОНЕЦПЕРИОДА(&ДатаОтчета, МЕСЯЦ)
            ТОГДА ЛОЖЬ
        ИНАЧЕ ИСТИНА
    КОНЕЦ
    ИЛИ &Парам

С точки зрения булевой логики эти два выражения полностью тождественны. Однако применение конструкции ВЫБОР ... КОГДА внутри секции ГДЕ крайне не рекомендуется по соображениям читаемости и оптимизации работы СУБД.

Оптимальные варианты записи логического условия

Разберем несколько способов упростить и сделать выражение более понятным и производительным.

  1. Использование оператора отрицания НЕ и оператора МЕЖДУ:

    Наиболее лаконичный и читабельный вариант без громоздких ветвлений:

    
    ГДЕ
        &Парам
        ИЛИ НЕ (Док.Дата МЕЖДУ НАЧАЛОПЕРИОДА(&ДатаОтчета, МЕСЯЦ) И КОНЕЦПЕРИОДА(&ДатаОтчета, МЕСЯЦ))
    
  2. Прямое разделение на два диапазона (до и после периода):

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

    
    ГДЕ
        &Парам
        ИЛИ Док.Дата < НАЧАЛОПЕРИОДА(&ДатаОтчета, МЕСЯЦ)
        ИЛИ Док.Дата > КОНЕЦПЕРИОДА(&ДатаОтчета, МЕСЯЦ)
    

Почему конструкции ВЫБОР и ИЛИ в секции ГДЕ снижают производительность

Проанализируем, как СУБД выполняет запросы с подобными предикатами:

  1. Потеря SARG-пригодности (SARGability): Использование ВЫБОР ... КОГДА в фильтрации транслируется в SQL-конструкцию CASE WHEN ... END. Для СУБД это вычисляемое скалярное выражение, которое должно быть рассчитано для каждой строки таблицы. В таком случае оптимизатор СУБД не может выполнить поиск по индексу (Index Seek) по полю Дата, что приводит к полному сканированию таблицы (Table Scan / Clustered Index Scan).

  2. Проблема универсальных условий (&Парам ИЛИ ...): Передача управляющего булевого флага через ИЛИ мешает оптимизатору построить эффективный план запроса. На этапе компиляции плана неизвестно, какое значение примет параметр, из-за чего СУБД часто выбирает наименее выгодную стратегию сканирования.

  3. Отрицания и индексы: Любое условие с оператором НЕ плохо укладывается в структуру B-дерева индексов, так как индекс оптимизирован для поиска точных значений или непрерывных диапазонов, а не «всего, кроме заданного интервала».

Особенности трехзначной логики и работа с NULL

Выясним причину возможного расхождения результатов при работе с левыми соединениями. В SQL действует трехзначная логика: ИСТИНА, ЛОЖЬ, NULL.

Если поле Док.Дата содержит NULL (например, когда таблица документов присоединена через ЛЕВОЕ СОЕДИНЕНИЕ), то сравнение Док.Дата < ДатаНачала ИЛИ Док.Дата > ДатаКонца вернет NULL, что в секции ГДЕ трактуется как ЛОЖЬ. Если необходимо, чтобы такие записи оставались в выборке, условие следует явно дополнить:


ГДЕ
    &Парам
    ИЛИ Док.Дата ЕСТЬ NULL
    ИЛИ НЕ (Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода)

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

Чтобы избежать просадки производительности на больших объемах данных, применим следующие подходы:

  1. Динамическое формирование текста запроса во встроенном языке:

    Если параметр &Парам включает или выключает фильтрацию, оптимальнее управлять текстом запроса до его выполнения в коде 1С, избегая оператора ИЛИ в SQL:

    
    ТекстЗапроса = 
    "ВЫБРАТЬ
    |   Док.Ссылка КАК Ссылка
    |ИЗ
    |   Документ.РеализацияТоваровУслуг КАК Док";
    
    Если Не ОтключитьФильтр Тогда
        ТекстЗапроса = ТекстЗапроса + "
        |ГДЕ
        |   НЕ (Док.Дата МЕЖДУ &НачалоПериода И &КонецПериода)";
    КонецЕсли;
    
  2. Разделение через ОБЪЕДИНИТЬ ВСЕ:

    Если требуется выбрать данные вне периода по индексированному полю в высоконагруженной таблице, эффективнее объединить две выборки с простыми условиями:

    
    ВЫБРАТЬ
        Док.Ссылка КАК Ссылка
    ИЗ
        Документ.РеализацияТоваровУслуг КАК Док
    ГДЕ
        Док.Дата < &НачалоПериода
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        Док.Ссылка КАК Ссылка
    ИЗ
        Документ.РеализацияТоваровУслуг КАК Док
    ГДЕ
        Док.Дата > &КонецПериода
    
  3. Использование Системы компоновки данных (СКД):

    В отчетах и динамических списках рекомендуется настраивать отборы через параметры и отборы СКД. Компоновщик автоматически исключает неактивные условия из итогового SQL-запроса, обеспечивая максимальную скорость выполнения.

← На главную