Что эффективнее в запросах 1С: использование временных таблиц или оператор «В» с параметром-массивом?

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

При разработке запросов в системе «1С:Предприятие 8» программисты часто сталкиваются с дилеммой: использовать ли простую конструкцию ГДЕ Ссылка В (&Список) или же поместить данные во временную таблицу и выполнить ВНУТРЕННЕЕ СОЕДИНЕНИЕ. На первый взгляд, второй вариант кажется избыточным и сложным, особенно если данных немного. Однако производительность системы на больших объемах может существенно измениться. Разберем детально, как работают эти механизмы, проведем сравнение и выясним, где находится «точка невозврата» для каждого метода.

Суть проблемы: два подхода к одной задаче

Рассмотрим стандартную ситуацию: у нас есть таблица значений тбзДанные с кодами товаров, и нам нужно выбрать ссылки на номенклатуру из справочника, соответствующие этим кодам. Проанализируем два варианта реализации этого запроса.

Вариант 1. Использование временной таблицы (ВТ)

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


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

Вариант 2. Использование оператора «В»

Здесь мы напрямую передаем список (массив или таблицу значений) в условие отбора ГДЕ.


// Текст запроса с оператором В
"ВЫБРАТЬ
|	СправочникНоменклатура.Ссылка КАК НоменклатураСсылка,
|	СправочникНоменклатура.асИдентификаторГЛ КАК асИдентификаторГЛ
|ИЗ
|	Справочник.Номенклатура КАК СправочникНоменклатура
|ГДЕ
|	СправочникНоменклатура.асИдентификаторГЛ В (&ТбзДанные)"

Сравнение производительности и «порог 130 строк»

Проанализируем результаты практических замеров. При небольшом количестве строк в исходной таблице (например, до 100 строк) второй вариант с оператором В работает быстрее. Это объясняется тем, что системе не нужно тратить время на создание временной таблицы в базе данных (СУБД), выделение под неё памяти и выполнение транзакционных операций по записи данных.

Однако ситуация кардинально меняется при увеличении объема данных. Экспериментальным путем выяснено, что порог эффективности находится в районе 130 элементов:

  1. Если в списке меньше 130 строк: Вариант с оператором В может быть в 3 раза быстрее, чем вариант с временной таблицей.
  2. Если в списке больше 130 строк: Эффективность временной таблицы резко возрастает. При превышении этого порога запрос с ВТ работает в 2 и более раз быстрее.

С ростом количества элементов в списке (до тысяч и десятков тысяч) разрыв в скорости становится колоссальным. Использование В (&Параметр) на огромных списках может привести к крайне медленной работе или даже к падению запроса с ошибкой СУБД.

Почему так происходит? Взгляд на уровне SQL

Чтобы понять причины такой разницы, разберем, как платформа 1С транслирует эти запросы в язык SQL, понятный для сервера базы данных (например, MS SQL Server или PostgreSQL).

1. Механизм оператора «В» (IN)

Когда мы пишем ГДЕ Поле В (&Параметр), платформа 1С превращает это в SQL-конструкцию WHERE Field IN (@P1, @P2, ..., @PN). Для СУБД это выглядит как перечисление конкретных значений. Оптимизатор запросов СУБД в этом случае чаще всего использует алгоритм Nested Loops (Вложенные циклы). Он берет каждое значение из вашего списка и ищет его в индексе справочника. Если список маленький — это быстро. Если список большой — количество «циклов» поиска становится огромным.

2. Механизм Временных Таблиц

При использовании ПОМЕСТИТЬ 1С создает реальную временную таблицу в системной базе данных СУБД (например, tempdb). После заполнения этой таблицы данными выполняется полноценное соединение таблиц. В этом случае оптимизатор СУБД может применить более мощные алгоритмы, такие как Hash Join или Merge Join. Эти методы гораздо эффективнее обрабатывают большие наборы данных, так как они работают с массивами данных целиком, а не перебирают их по одному элементу.

Проблема кэша планов запросов (Plan Cache Bloating)

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

В случае с оператором В, если сегодня вы передали 5 элементов, а завтра 10, текст SQL-запроса изменится (количество параметров @P будет разным). СУБД сочтет это за новый запрос и будет заново строить план выполнения, что расходует ресурсы сервера. Временные таблицы избавляют от этой проблемы: текст основного запроса к базе остается неизменным независимо от того, сколько строк вы положили во временную таблицу.

Ограничение на количество параметров

Не стоит забывать и о жестких ограничениях СУБД. Например, MS SQL Server поддерживает не более 2100 параметров в одном запросе. Если вы попытаетесь передать в оператор В массив, содержащий 3000 элементов, запрос завершится с ошибкой. Временные таблицы не имеют такого ограничения, так как данные передаются не через параметры текста запроса, а через механизм загрузки данных в таблицу.

Индексирование временных таблиц

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


"ВЫБРАТЬ
|	ТбзДанные.КодТовара КАК КодТовара
|ПОМЕСТИТЬ втДанные
|ИЗ
|	&ТбзДанные КАК ТбзДанные
|;
|
|////////////////////////////////////////////////////////////////
|// Создаем индекс по полю связи
|ИНДЕКСИРОВАТЬ ПО
|	КодТовара
|;
|
|////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	СправочникНоменклатура.Ссылка КАК НоменклатураСсылка,
|	втДанные.КодТовара КАК КодТовара
|ИЗ
|	Справочник.Номенклатура КАК СправочникНоменклатура
|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ втДанные КАК втДанные
|		ПО СправочникНоменклатура.асИдентификаторГЛ = втДанные.КодТовара"

Без индексации временной таблицы СУБД придется выполнять полное сканирование этой таблицы при каждом соединении, что может нивелировать все преимущества её использования на средних объемах данных.

Типы данных: Таблица значений или Массив?

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


// Правильный подход
МассивКодов = ТбзДанные.ВыгрузитьКолонку("КодТовара");
Запрос.УстановитьПараметр("СписокКодов", МассивКодов);

Это делает код более предсказуемым и защищает от ошибок, если структура таблицы значений изменится и нужная колонка перестанет быть первой.

Итоговые рекомендации

Подведем итог нашему анализу и определим правила выбора архитектуры запроса:

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

← На главную