Как правильно использовать конструкцию ВЫБОР в условии ГДЕ запроса 1С

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

При написании сложных запросов в системе 1С:Предприятие разработчики часто сталкиваются с необходимостью реализовать гибкую логику отбора данных, зависящую от переданных параметров. Одним из инструментов для этого кажется использование конструкции ВЫБОР непосредственно в секции ГДЕ. Однако этот подход таит в себе как логические особенности, так и серьезные риски для производительности системы. В этой статье мы подробно разберем, как работает эта конструкция, и выясним, почему для динамических списков существуют более предпочтительные решения.

Логика выполнения конструкции ВЫБОР

Рассмотрим классическую ситуацию, когда в запрос передается несколько флагов, определяющих режим фильтрации. Например, нам нужно вывести заказы либо по конкретному сотруднику, либо по пользователю, либо все сразу. Автор темы привел следующий пример кода:


ВЫБРАТЬ
    ЗаказПокупателя.Номер,
    ЗаказПокупателя.Дата
ИЗ
    Документ.ЗаказПокупателя КАК ЗаказПокупателя
ГДЕ
    ВЫБОР 
        КОГДА &ВыводитьВсе ТОГДА ИСТИНА   
        КОГДА &ВыводитьПоСотруднику ТОГДА Сотрудник = &Сотрудник  
        КОГДА &ВыводитьПоПользователю ТОГДА Пользователь = &Пользователь
        ИНАЧЕ ЛОЖЬ
    КОНЕЦ

Основной вопрос заключается в том, что произойдет, если несколько параметров (например, &ВыводитьВсе и &ВыводитьПоСотруднику) одновременно примут значение ИСТИНА. Проанализируем поведение системы: конструкция ВЫБОР в языке запросов 1С (как и оператор CASE в SQL) работает по принципу первого успешного совпадения. Это означает, что условия проверяются строго последовательно сверху вниз.

Как только система находит ветку КОГДА, условие которой истинно, она вычисляет результат этой ветки и прекращает дальнейшие проверки внутри этой конструкции ВЫБОР. Таким образом, если параметр &ВыводитьВсе равен ИСТИНА, то все остальные условия (по сотруднику или пользователю) будут проигнорированы, даже если соответствующие параметры также установлены. Это поведение полностью идентично конструкции ЕСЛИ...ИНАЧЕЕСЛИ...КОНЕЦЕСЛИ в языке 1С.

Влияние на производительность и SARGability запросов

Несмотря на кажущееся удобство, использование ВЫБОР в секции ГДЕ часто считается плохой практикой. Разберем причины этого явления. Основная проблема заключается в том, как СУБД (SQL Server, PostgreSQL) строит план выполнения запроса. Для обеспечения высокой скорости поиска по индексам условия в запросе должны быть SARGable (Search Argumentable).

Когда условие обернуто в ВЫБОР, оптимизатор СУБД зачастую не может заранее определить, какое поле будет использоваться для фильтрации. В результате вместо эффективного поиска по индексу (Index Seek) система может прибегнуть к полному сканированию таблицы (Full Scan), что на больших объемах данных (например, в таблице заказов за несколько лет) приведет к значительным задержкам.

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


ГДЕ
    (&ВыводитьВсе = ИСТИНА)
    ИЛИ (&ВыводитьПоСотруднику = ИСТИНА И Сотрудник = &Сотрудник)
    ИЛИ (&ВыводитьПоПользователю = ИСТИНА И Пользователь = &Пользователь)

Такая запись более прозрачна для оптимизатора, однако и она не всегда идеальна. Лучшим решением является динамическое формирование текста запроса или использование механизмов компоновки данных.

Особенности работы с динамическими списками

Если ваша задача связана с отображением данных в форме (динамический список), использование ВЫБОР в тексте запроса крайне не рекомендуется. Динамические списки в 1С работают на базе Системы компоновки данных (СКД), которая обладает встроенными механизмами управления отборами.

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


// Пример программной установки отбора в динамическом списке
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("Сотрудник", Объект.НужныйСотрудник);

ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(
    Список, 
    "Сотрудник", 
    Объект.НужныйСотрудник, 
    ВидСравненияКомпоновкиДанных.Равно, 
    "Отбор по сотруднику", 
    ЗначениеЗаполнено(Объект.НужныйСотрудник)
);

Почему этот метод лучше? Когда вы устанавливаете отбор через объект КомпоновщикНастроек, платформа 1С сама модифицирует итоговый SQL-запрос. Если отбор выключен, условие вообще не попадет в текст WHERE, что позволит СУБД построить максимально эффективный план выполнения для текущей ситуации.

Оптимизация обращений через точку и составных типов

В процессе обсуждения на форуме была затронута еще одна важная проблема — производительность отборов по полям составного типа или при обращении через точку (например, Объект.Организация). Выясним, почему это может вызывать торможения:

  1. Неявные соединения (JOIN): Каждое обращение через точку в условии ГДЕ превращается в левое соединение с другой таблицей.
  2. Множественные соединения для составных типов: Если поле имеет составной тип, система вынуждена соединяться со всеми таблицами, входящими в этот тип, что порождает огромные и медленные SQL-запросы.

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


// Плохо:
ГДЕ Документ.Владелец.Наименование = &Имя

// Хорошо:
ГДЕ ВЫРАЗИТЬ(Документ.Владелец КАК Справочник.Контрагенты).Наименование = &Имя

Подведем итоги

Проанализировав ситуацию, мы можем сделать следующие выводы для качественной разработки на платформе 1С:

Соблюдение этих правил позволит вам создавать не только логически правильные, но и высокопроизводительные решения, которые будут стабильно работать даже на системах с миллионами записей.

← На главную