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