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