Почему запрос с условием "В (&Параметр)" работает медленно и как его оптимизировать?

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

Часто при разработке на 1С мы сталкиваемся с ситуацией, когда казалось бы простой запрос начинает выполняться непозволительно долго. Давайте разберем классическую проблему производительности: запрос, содержащий условие В (&Параметр), где в качестве параметра передается список значений. В нашем примере запрос к регистру сведений с 11 элементами в списке выполнялся 7 секунд. Однако, стоило заменить условие на явное перечисление через ИЛИ (например, Поле = &П1 ИЛИ Поле = &П2...), как время выполнения сократилось до 1 секунды. Выясним причину такого поведения и проанализируем эффективные способы оптимизации.

Почему конструкция "В" работает медленнее, чем "ИЛИ"

Проблема кроется не в самой платформе 1С, а в механике работы оптимизатора базы данных (СУБД), например, MS SQL Server. Рассмотрим подробнее, что происходит "под капотом":

  1. Проблема кэша планов выполнения (Plan Cache Bloat). Если количество элементов в передаваемом списке постоянно меняется (сейчас 11, потом 5, затем 20), СУБД воспринимает каждый такой запрос как уникальный из-за разного количества параметров IN (@p1, @p2, ... @pN). Это заставляет сервер заново компилировать план, тратя ресурсы процессора.
  2. Parameter Sniffing (чувствительность к параметрам). Оптимизатор строит план выполнения на основе первого переданного значения. Если сначала пришел 1 элемент, строится быстрый план поиска (Index Seek). Если затем приходит 11, СУБД пытается использовать старый план, что может вызвать катастрофическое падение скорости.
  3. Оценка кардинальности. Оптимизатору SQL очень сложно правильно оценить распределение данных для динамического набора параметров. Часто вместо быстрого поиска по индексу он выбирает полное сканирование таблицы (Index Scan), что на больших регистрах сведений занимает секунды.

В то же время, когда мы жестко прописываем цепочку ИЛИ, оптимизатор более предсказуемо преобразует эти условия в операцию множественного поиска по индексу с последующим объединением результатов, что работает стабильно быстро.

Способ 1: Использование Схемы Запроса (программное формирование "ИЛИ")

Разберем решение, к которому пришел автор темы. Если список формируется динамически, мы можем программно сгенерировать текст запроса, заменив одно условие В на множество условий ИЛИ. Чтобы сделать это красиво и надежно, воспользуемся объектом СхемаЗапроса.

Этот подход отлично работает на небольших списках (10-15 элементов) и действительно снижает время выполнения до минимума. Однако он имеет минус: разбор AST-дерева через схему запроса создает небольшую дополнительную нагрузку на сервер приложений 1С.

Способ 2: Временная таблица из Таблицы Значений (Лучшая практика 1С)

Посмотрим на самый правильный и стабильный стандарт разработки для подобных задач — передачу Таблицы Значений (ТЗ) в качестве параметра запроса. Это устраняет любые проблемы с кэшем планов СУБД.

Вместо динамического формирования условий, мы помещаем наши элементы в типизированную таблицу на сервере и передаем ее в запрос. Рассмотрим пример кода:


// 1. Создаем типизированную ТЗ на стороне 1С
ТЗ_Процессы = Новый ТаблицаЗначений;
// Важно явно указать тип колонки, чтобы избежать ошибок СУБД
ТЗ_Процессы.Колонки.Добавить("БизнесПроцесс", Новый ОписаниеТипов("ДокументСсылка.Задание"));

Для Каждого Процесс Из СписокПроцессов Цикл
    ТЗ_Процессы.Добавить().БизнесПроцесс = Процесс;
КонецЦикла;

// 2. Формируем один статический запрос
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЗ.БизнесПроцесс КАК БизнесПроцесс
    |ПОМЕСТИТЬ ВТ_Процессы
    |ИЗ
    |	&ТЗ_Процессы КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ РАЗРЕШЕННЫЕ
    |	ДанныеБизнесПроцессов.ГлавнаяЗадача КАК ГлавнаяЗадача,
    |	ДанныеБизнесПроцессов.ВедущаяЗадача КАК ВедущаяЗадача,
    |	ДанныеБизнесПроцессов.БизнесПроцесс КАК Ссылка,
    |	ДанныеБизнесПроцессов.Наименование КАК Наименование,
    |	ДанныеБизнесПроцессов.Завершен КАК Выполнена,
    |	ДанныеБизнесПроцессов.Дата КАК Дата,
    |	ДанныеБизнесПроцессов.ПометкаУдаления КАК ПометкаУдаления
    |ИЗ
    |	ВТ_Процессы КАК ВТ_Процессы
    |		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ДанныеБизнесПроцессов КАК ДанныеБизнесПроцессов
    |		ПО ВТ_Процессы.БизнесПроцесс = ДанныеБизнесПроцессов.БизнесПроцесс
    |УПОРЯДОЧИТЬ ПО
    |	Дата";

Запрос.УстановитьПараметр("ТЗ_Процессы", ТЗ_Процессы);
Результат = Запрос.Выполнить();

Преимущества этого метода:

  1. Текст запроса статичен, накладные расходы на перекомпиляцию плана отсутствуют.
  2. СУБД строит оптимальный план соединения (Hash Match или Merge Join) с временной таблицей, что работает мгновенно независимо от количества переданных элементов.

Особенности индексации в Регистрах Сведений

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

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

Влияние типа коллекции: Список Значений или Массив?

Часто разработчики передают в запрос СписокЗначений. С точки зрения СУБД разницы не будет, так как оба объекта разворачиваются в одинаковый SQL-код. Но для платформы 1С разница есть. СписокЗначений — это интерфейсный объект, который хранит представления, картинки и пометки. Его сериализация и передача между клиентом и сервером занимает больше памяти и времени. Возьмите за правило: в параметрах запросов следует использовать исключительно Массив или ТаблицуЗначений.

Резюме: внутренний порог 1С

Интересно отметить, что в платформе 1С есть встроенный механизм оптимизации: если в массиве или списке более 128 элементов, платформа неявно (под капотом) сама создает временную таблицу и выполняет соединение. Проблемы с конструкцией В возникают именно на коротких списках (как у автора — 11 элементов), когда этот порог не достигнут, и запрос уходит в СУБД в виде IN. Принудительное использование Таблицы Значений избавляет нас от этих "сюрпризов" оптимизатора.

← На главную