Как оптимизировать запрос 1С при использовании больших массивов в условиях?

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

В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда необходимо выбрать данные, отфильтровав их по внушительному списку ссылок (например, массив из нескольких тысяч элементов). Стандартный подход с передачей массива в параметр условия В (&Массив) при увеличении количества элементов начинает демонстрировать значительное снижение производительности. В этой статье мы подробно разберем, почему это происходит, и проанализируем наиболее эффективные способы решения данной проблемы.

Выясним причину: почему массивы в параметрах работают медленно

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

  1. Ограничения СУБД: Многие системы управления базами данных (например, Microsoft SQL Server или PostgreSQL) имеют ограничения на количество элементов в списке IN или на общий размер текста запроса.
  2. План запроса: При передаче огромного списка значений оптимизатор СУБД может построить неоптимальный план выполнения, что приведет к полному сканированию таблицы (Table Scan) вместо использования индексов. Чтобы разобраться в этом глубже, полезно научиться анализировать SQL сервер глазами 1С-ника и понимать, как он работает.
  3. Раздувание кэша планов: Каждое изменение количества элементов в массиве заставляет СУБД заново компилировать запрос, что переполняет кэш планов и снижает общую производительность системы.

Решение №1: Использование временных таблиц и индексации

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

Рассмотрим пример кода, в котором мы создаем МенеджерВременныхТаблиц и подготавливаем данные:


МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Помещаем массив во временную таблицу
Запрос.Текст = 
    "ВЫБРАТЬ
    |    &МассивСсылок КАК Ссылка
    |ПОМЕСТИТЬ ВТ_Фильтр
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    Номенклатура.Ссылка КАК Ссылка,
    |    Номенклатура.Код КАК Код
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |    Номенклатура.Ссылка В (ВЫБРАТЬ ВТ.Ссылка ИЗ ВТ_Фильтр КАК ВТ)";

Запрос.УстановитьПараметр("МассивСсылок", МассивОбъектов);
Результат = Запрос.Выполнить();

Однако простого помещения в таблицу недостаточно для максимального ускорения. Важно использовать индексацию. Этот принцип касается не только временных таблиц, но и всей базы данных в целом. Проанализируем, как изменится текст запроса при добавлении индекса:


Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЗ.Ссылка КАК Ссылка
    |ПОМЕСТИТЬ ВТ_Фильтр
    |ИЗ
    |    &ТаблицаЗначений КАК ТЗ
    |Индексировать По
    |    Ссылка
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    Номенклатура.Наименование
    |ИЗ
    |    Справочник.Номенклатура КАК Номенклатура
    |ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Фильтр КАК ВТ_Фильтр
    |ПО Номенклатура.Ссылка = ВТ_Фильтр.Ссылка";

Обратите внимание, что мы использовали ВНУТРЕННЕЕ СОЕДИНЕНИЕ вместо условия ГДЕ ... В (...). Это позволяет оптимизатору СУБД работать гораздо эффективнее, используя индекс по полю Ссылка во временной таблице.

Решение №2: Использование Таблицы Значений как параметра запроса

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

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


ТЗФильтр = Новый ТаблицаЗначений;
ТЗФильтр.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));

Для Каждого ЭлементИзМассива Из МассивОбъектов Цикл
    НоваяСтрока = ТЗФильтр.Добавить();
    НоваяСтрока.Ссылка = ЭлементИзМассива;
КонецЦикла;

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЗ.Ссылка КАК Ссылка
    |ПОМЕСТИТЬ ВТ_Данные
    |ИЗ
    |    &ТЗ КАК ТЗ
    |ИНДЕКСИРОВАТЬ ПО
    |    Ссылка;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    Товары.Ссылка,
    |    Товары.Цена
    |ИЗ
    |    Справочник.Номенклатура КАК Товары
    |ГДЕ
    |    Товары.Ссылка В (ВЫБРАТЬ ВТ.Ссылка ИЗ ВТ_Данные КАК ВТ)";

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

Сравним подходы: когда какой метод эффективнее?

Проанализируем ситуацию в зависимости от объема данных:

  1. Массив до 100 элементов: Можно использовать стандартное условие В (&Массив). Разница в производительности будет ничтожной, а код останется лаконичным.
  2. Массив от 100 до 1000 элементов: Рекомендуется начать использовать временные таблицы, так как это стабилизирует время выполнения запроса и предотвращает проблемы с планами СУБД.
  3. Массив более 1000 элементов: Использование временной таблицы с обязательной индексацией становится критически важным. Без индекса соединение больших таблиц может занять длительное время.

Важные рекомендации по работе с Менеджером временных таблиц

Рассмотрим несколько нюансов, которые помогут избежать типичных ошибок при работе с МенеджерВременныхТаблиц. Кстати, функционал работы с временными таблицами постоянно развивается, и с недавних пор платформа позволяет добавлять данные в существующую временную таблицу, что открывает новые возможности.

Во-первых, всегда помните о времени жизни временных таблиц. Они существуют в базе данных до тех пор, пока объект МенеджерВременныхТаблиц не будет уничтожен или пока вы явно не удалите их командой УНИЧТОЖИТЬ. Если вы выполняете много запросов в цикле с одним менеджером, это может привести к переполнению базы данных tempdb на сервере. Чтобы этого избежать, важна своевременная и корректная настройка регламентных операций MS SQL Server, которая поможет поддерживать СУБД в рабочем состоянии.

Во-вторых, старайтесь избегать помещения во временные таблицы лишних полей. Чем "уже" таблица (меньше колонок), тем быстрее она будет создана и проиндексирована. Если вам нужны только ссылки, не добавляйте туда коды или наименования для "удобства".

В-третьих, проанализируем использование метода Закрыть(). Если менеджер вам больше не нужен, хорошим тоном будет вызвать МенеджерВТ.Закрыть(), чтобы освободить ресурсы сервера не дожидаясь срабатывания сборщика мусора.


// Пример правильного завершения работы с ВТ
Запрос.МенеджерВременныхТаблиц.Закрыть();

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

Мы рассмотрели проблему производительности при использовании больших массивов в запросах 1С. Основной вывод: массивы в параметрах хороши для малых выборок, а для серьезных задач следует использовать временные таблицы. Использование МенеджерВременныхТаблиц, типизация входящей ТаблицыЗначений и обязательная индексация полей — это три столпа, на которых строится работа с высоконагруженными запросами в 1С. Применяя эти правила, мы обеспечиваем стабильную и быструю работу конфигурации даже при обработке огромных массивов данных.

← На главную