В практике разработки на платформе 1С:Предприятие 8.3 часто возникает ситуация, когда необходимо выбрать данные, отфильтровав их по внушительному списку ссылок (например, массив из нескольких тысяч элементов). Стандартный подход с передачей массива в параметр условия В (&Массив) при увеличении количества элементов начинает демонстрировать значительное снижение производительности. В этой статье мы подробно разберем, почему это происходит, и проанализируем наиболее эффективные способы решения данной проблемы.
Проанализируем механику работы платформы и СУБД. Когда мы передаем массив в запрос через оператор В, платформа 1С транслирует этот массив в SQL-запрос как перечисление значений через запятую или создает длинный список условий. Рассмотрим основные проблемы этого подхода:
IN или на общий размер текста запроса.Наиболее правильным и рекомендуемым методом является использование временных таблиц. Разберем по шагам, как правильно реализовать этот механизм. Для отладки и тестирования подобных конструкций удобно использовать продвинутую консоль запросов, которая поддерживает временные таблицы. Вместо того чтобы передавать массив напрямую в условие, мы сначала поместим его во временную таблицу, проиндексируем её, а затем соединим с основной таблицей запроса.
Рассмотрим пример кода, в котором мы создаем МенеджерВременныхТаблиц и подготавливаем данные:
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;
// Помещаем массив во временную таблицу
Запрос.Текст =
"ВЫБРАТЬ
| &МассивСсылок КАК Ссылка
|ПОМЕСТИТЬ ВТ_Фильтр
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Код КАК Код
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (ВЫБРАТЬ ВТ.Ссылка ИЗ ВТ_Фильтр КАК ВТ)";
Запрос.УстановитьПараметр("МассивСсылок", МассивОбъектов);
Результат = Запрос.Выполнить();
Однако простого помещения в таблицу недостаточно для максимального ускорения. Важно использовать индексацию. Этот принцип касается не только временных таблиц, но и всей базы данных в целом. Проанализируем, как изменится текст запроса при добавлении индекса:
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТ_Фильтр
|ИЗ
| &ТаблицаЗначений КАК ТЗ
|Индексировать По
| Ссылка
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Фильтр КАК ВТ_Фильтр
|ПО Номенклатура.Ссылка = ВТ_Фильтр.Ссылка";
Обратите внимание, что мы использовали ВНУТРЕННЕЕ СОЕДИНЕНИЕ вместо условия ГДЕ ... В (...). Это позволяет оптимизатору СУБД работать гораздо эффективнее, используя индекс по полю Ссылка во временной таблице.
Начиная с версии платформы 8.3.3, мы можем передавать Таблицу Значений напрямую в качестве параметра запроса для создания временной таблицы. Это значительно упрощает код и работает быстрее, чем передача массива. Разберем этот алгоритм подробнее.
Сначала подготовим таблицу значений с нужной структурой. Важно, чтобы типы колонок были строго типизированы, иначе 1С не сможет создать корректную временную таблицу в базе данных.
ТЗФильтр = Новый ТаблицаЗначений;
ТЗФильтр.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Для Каждого ЭлементИзМассива Из МассивОбъектов Цикл
НоваяСтрока = ТЗФильтр.Добавить();
НоваяСтрока.Ссылка = ЭлементИзМассива;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
| ТЗ.Ссылка КАК Ссылка
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТЗ КАК ТЗ
|ИНДЕКСИРОВАТЬ ПО
| Ссылка;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Товары.Ссылка,
| Товары.Цена
|ИЗ
| Справочник.Номенклатура КАК Товары
|ГДЕ
| Товары.Ссылка В (ВЫБРАТЬ ВТ.Ссылка ИЗ ВТ_Данные КАК ВТ)";
Запрос.УстановитьПараметр("ТЗ", ТЗФильтр);
Результат = Запрос.Выполнить();
Проанализируем ситуацию в зависимости от объема данных:
В (&Массив). Разница в производительности будет ничтожной, а код останется лаконичным.Рассмотрим несколько нюансов, которые помогут избежать типичных ошибок при работе с МенеджерВременныхТаблиц. Кстати, функционал работы с временными таблицами постоянно развивается, и с недавних пор платформа позволяет добавлять данные в существующую временную таблицу, что открывает новые возможности.
Во-первых, всегда помните о времени жизни временных таблиц. Они существуют в базе данных до тех пор, пока объект МенеджерВременныхТаблиц не будет уничтожен или пока вы явно не удалите их командой УНИЧТОЖИТЬ. Если вы выполняете много запросов в цикле с одним менеджером, это может привести к переполнению базы данных tempdb на сервере. Чтобы этого избежать, важна своевременная и корректная настройка регламентных операций MS SQL Server, которая поможет поддерживать СУБД в рабочем состоянии.
Во-вторых, старайтесь избегать помещения во временные таблицы лишних полей. Чем "уже" таблица (меньше колонок), тем быстрее она будет создана и проиндексирована. Если вам нужны только ссылки, не добавляйте туда коды или наименования для "удобства".
В-третьих, проанализируем использование метода Закрыть(). Если менеджер вам больше не нужен, хорошим тоном будет вызвать МенеджерВТ.Закрыть(), чтобы освободить ресурсы сервера не дожидаясь срабатывания сборщика мусора.
// Пример правильного завершения работы с ВТ
Запрос.МенеджерВременныхТаблиц.Закрыть();
Мы рассмотрели проблему производительности при использовании больших массивов в запросах 1С. Основной вывод: массивы в параметрах хороши для малых выборок, а для серьезных задач следует использовать временные таблицы. Использование МенеджерВременныхТаблиц, типизация входящей ТаблицыЗначений и обязательная индексация полей — это три столпа, на которых строится работа с высоконагруженными запросами в 1С. Применяя эти правила, мы обеспечиваем стабильную и быструю работу конфигурации даже при обработке огромных массивов данных.