Оптимизация запроса в 1С: массив или временная таблица?

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

При разработке на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с дилеммой: передать список ссылок (например, массив контрагентов) напрямую в параметр запроса или предварительно поместить эти данные во временную таблицу. Особенно остро этот вопрос встает, когда количество элементов в списке исчисляется тысячами. Разберем детально, как работает платформа «под капотом», и выясним, какую стратегию выбрать для оптимальной производительности.

Механизм автоматической оптимизации платформы

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Контрагенты.Ссылка КАК Ссылка,
    |	Контрагенты.ИНН КАК ИНН
    |ИЗ
    |	Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |	Контрагенты.Ссылка В (&СписокКонтрагентов)";

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

Многие разработчики опасаются, что при большом количестве элементов в МассивСсылок (например, 10 000 записей), СУБД получит гигантский запрос с перечислением всех идентификаторов через запятую. Однако проанализируем поведение платформы 1С. Начиная с определенных версий, 1С применяет встроенную оптимизацию:

  1. Если в массиве мало элементов (обычно до 128), платформа формирует прямой SQL-запрос с условием IN (guid1, guid2, ...).
  2. Если количество элементов превышает порог (в современных версиях это 128 или 256 элементов), платформа автоматически создает временную таблицу на стороне СУБД, наполняет её данными из массива и заменяет условие В (&Параметр) на внутреннее соединение с этой таблицей.

Таким образом, даже если мы передадим огромный массив, платформа постарается не «уронить» SQL-сервер слишком длинным текстом запроса. Но значит ли это, что об оптимизации можно забыть? Выясним это далее.

Почему ручное использование временных таблиц лучше?

Несмотря на автоматику, использование МенеджерВременныхТаблиц часто оказывается более эффективным и «чистым» решением. Рассмотрим ситуацию, когда данные для фильтрации уже получены в ходе выполнения программы другим запросом. Проанализируем типичную ошибку legacy-кода: выгрузка результата первого запроса в массив и передача его во второй запрос.

Рассмотрим пример «плохого» кода:


// Первый запрос получает список нужных ссылок
Результат1 = Запрос1.Выполнить().Выгрузить();
МассивСсылок = Результат1.ВыгрузитьКолонку("Ссылка");

// Второй запрос использует этот массив
Запрос2.Текст = "ВЫБРАТЬ ... ГДЕ Ссылка В (&МассивСсылок)";
Запрос2.УстановитьПараметр("МассивСсылок", МассивСсылок);

В этой схеме данные совершают лишний круг: СУБД -> Сервер 1С (память rphost) -> СУБД. Это нерационально расходует оперативную память сервера приложений и создает лишнюю сетевую нагрузку. Гораздо правильнее использовать один пакетный запрос с помещением данных во временную таблицу.

Посмотрим на оптимизированный вариант:


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

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

Запрос.УстановитьПараметр("ТЗ_Данных", ТаблицаСДанными);
Запрос.Выполнить();

Проблема индексации и статистики

При работе с очень большими объемами данных во временных таблицах (десятки и сотни тысяч строк) критически важно помнить про индексацию. Если мы просто поместим данные в ВТ, СУБД может выбрать неоптимальный план соединения (например, Nested Loops вместо Hash Match), так как у временной таблицы нет статистики.

Чтобы помочь планировщику запросов:

  1. Используйте ключевое слово ИНДЕКСИРОВАТЬ ПО в тексте запроса при создании ПОМЕСТИТЬ.
  2. Разбивайте пакет запросов. Если ВТ очень большая, лучше создать её в одном вызове Выполнить(), а использовать в другом. Это заставит СУБД обновить статистику по временной таблице.

Проанализируем пример с индексацией:


Запрос.Текст = 
    "ВЫБРАТЬ
    |	Ссылка
    |ПОМЕСТИТЬ ВТ_Список
    |ИЗ
    |	&Массив
    |ИНДЕКСИРОВАТЬ ПО
    |	Ссылка";

Нюансы различных СУБД

Разберем, как разные системы ведут себя при передаче больших списков параметров:

Подведем итоги и сформируем рекомендации

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

  1. Если список элементов фиксирован или невелик (до 100 элементов), смело используйте массив и условие ГДЕ ... В (&Список). Это просто и наглядно.
  2. Если вы работаете с Legacy-кодом, где функции возвращают массивы, и производительность вас устраивает — можно не трогать этот участок, так как платформа сама подменит массив на ВТ при больших объемах.
  3. Если вы пишете новый функционал или оптимизируете тяжелый отчет, всегда отдавайте предпочтение временным таблицам. Это гарантирует стабильный план запроса и предсказуемое поведение СУБД.
  4. Никогда не выгружайте результат одного запроса в массив только для того, чтобы передать его в другой запрос. Используйте пакетные запросы (символ ; в тексте запроса) и временные таблицы.
  5. При передаче готовой ТаблицыЗначений в запрос в качестве параметра, всегда сразу делайте ПОМЕСТИТЬ ВТ_Название с индексацией по полям связи.

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

← На главную