При разработке сложных отчетов или выгрузок данных (например, номенклатуры с остатками, ценами и свойствами) программисты часто сталкиваются с дилеммой: написать несколько отдельных запросов или объединить их в один пакет — для этого есть универсальная выгрузка любых данных через произвольные запросы. Рассмотрим ситуацию, когда необходимо получить данные по разным блокам, которые логически связаны, но структурно различны. Разберем подробно, почему механизм пакетов запросов является предпочтительным с точки зрения производительности, надежности и архитектуры системы.
Проанализируем процесс взаимодействия сервера приложений 1С и сервера базы данных (СУБД). Каждый вызов метода Выполнить() для объекта Запрос инициирует отдельный сеанс связи (round-trip). Рассмотрим, что происходит при выполнении трех независимых запросов:
РезультатЗапроса.Если мы повторяем это трижды, мы трижды тратим ресурсы на установку соединения и передачу данных по сети. В случае использования пакета запросов все инструкции передаются одним блоком. Это существенно снижает сетевые задержки, что особенно критично в распределенных системах или при работе через медленные каналы связи. Посмотрим на это со стороны СУБД: ей гораздо проще оптимизировать выполнение, когда она видит всю последовательность действий целиком.
Выясним главную причину использования пакетов — работу с временными таблицами (ВТ). В 1С пакет запросов позволяет создавать промежуточные таблицы, которые живут в рамках одного выполнения. Это избавляет нас от необходимости выгружать промежуточные данные в ТаблицаЗначений на сторону сервера 1С, чтобы потом передавать их обратно в СУБД в качестве параметров следующего запроса.
Важный момент: внутри пакета мы можем использовать инструкцию ИНДЕКСИРОВАТЬ ПО. Рассмотрим пример:
// Внутри текста пакета запроса
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка
ПОМЕСТИТЬ ВТ_СписокТоваров
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ЭтоГруппа = ЛОЖЬ
ИНДЕКСИРОВАТЬ ПО
Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Цены.Цена,
ВТ_СписокТоваров.Ссылка
ИЗ
ВТ_СписокТоваров КАК ВТ_СписокТоваров
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Цены
ПО ВТ_СписокТоваров.Ссылка = Цены.Номенклатура
Проанализируем этот код: создание индекса по ссылке во временной таблице позволяет СУБД мгновенно выполнять соединение с регистром цен. Если бы мы делали это отдельными запросами без менеджера временных таблиц, нам пришлось бы передавать массив ссылок, что при большом объеме данных привело бы к раздуванию текста SQL-запроса и замедлению работы оптимизатора.
Разберем ситуацию, когда данные в базе быстро меняются. Если мы выполняем запрос к ценам в 12:00:01, а запрос к остаткам в 12:00:05, существует вероятность, что между этими событиями другое фоновое задание изменит остатки или цены. В итоге в вашем отчете или файле выгрузки данные "не сойдутся".
Пакет запросов выполняется СУБД максимально быстро, часто в рамках одного снимка данных. Это гарантирует, что все части пакета видят состояние базы данных на один и тот же момент времени. Таким образом, мы обеспечиваем целостность выгрузки без наложения избыточных блокировок на таблицы.
С точки зрения кода 1С, пакеты предоставляют удобный инструментарий. Метод ВыполнитьПакет() возвращает массив объектов РезультатЗапроса. Посмотрим на пример обработки результатов:
РезультатыПакет = Запрос.ВыполнитьПакет();
// Первый результат — например, основная номенклатура
ВыборкаШапка = РезультатыПакет[0].Выбрать();
// Второй результат — например, ее свойства
ТаблицаСвойств = РезультатыПакет[1].Выгрузить();
// Третий результат — цены
ТаблицаЦен = РезультатыПакет[2].Выгрузить();
Такой подход позволяет за один "поход" в базу данных собрать всю необходимую структуру. Существует также метод ВыполнитьПакетСПромежуточнымиДанными(), который крайне полезен при отладке сложных алгоритмов (поможет инструмент отладки кода и запросов без конфигуратора). Он позволяет получить содержимое всех временных таблиц, созданных внутри пакета, что невозможно при использовании одиночных запросов без дополнительных манипуляций.
Рассмотрим сравнение пакетов и объекта МенеджерВременныхТаблиц. Хотя МВТ позволяет передавать таблицы между разными методами и объектами Запрос, он требует ручного контроля. Если программист забудет вызвать метод Закрыть() или явно удалить таблицу через УНИЧТОЖИТЬ, временные таблицы будут занимать место в базе tempdb до конца сеанса.
В случае с пакетом запросов все временные таблицы уничтожаются автоматически сразу после завершения работы метода Выполнить() или ВыполнитьПакет(). Это гарантирует чистоту ресурсов и предотвращает переполнение дискового пространства на сервере СУБД при возникновении ошибок в коде.
Несмотря на то, что длинные запросы "на много экранов" могут показаться сложными для чтения, они гораздо прозрачнее, чем разрозненная логика в коде. В консоли запросов вы можете отладить весь пакет целиком, видя взаимосвязи между таблицами — для этого подойдёт консоль запросов и СКД для разработчика. В то время как при анализе кода с множеством отдельных запросов вам пришлось бы вручную сопоставлять, какие параметры куда передаются и какие промежуточные таблицы значений создаются в памяти сервера 1С.
Подведем итог: использование пакетов запросов — это не просто следование моде, а техническая необходимость в высоконагруженных системах — это обеспечит мониторинг производительности и анализ запросов 1С. Это позволяет: