Разработка и Конфигурация · 14 мин
Как выполнить пакет запросов в 1С
Как выполнить пакет запросов в 1С: синтаксис пакетных запросов, временные таблицы, обработка нескольких результатов, типичные ошибки и практические примеры кода.
Пакетный запрос в 1С — это несколько запросов, объединённых в одном тексте и выполняемых за одно обращение к СУБД. Вместо того чтобы писать три-четыре отдельных объекта «Запрос» и передавать между ними данные через таблицы значений, разработчик описывает всю логику в одном тексте: промежуточные выборки помещаются во временные таблицы, а финальный запрос возвращает готовый результат.
Такой подход сокращает количество обращений к базе, упрощает код и часто заметно ускоряет обработку больших объёмов данных. Особенно выигрыш заметен в клиент-серверном режиме, где каждый отдельный запрос — это сетевое взаимодействие между сервером 1С и СУБД.
Ниже разобран синтаксис пакетных запросов, работа со временными таблицами, способы получить каждый результат пакета, а также типичные ошибки, из-за которых пакетный запрос возвращает не те данные или падает с ошибкой.
Что такое пакетный запрос и когда он нужен
Пакетным называется запрос, в тексте которого содержится несколько операторов ВЫБРАТЬ, разделённых символом «;». Все они выполняются последовательно в рамках одного вызова. Между собой части пакета связываются временными таблицами: результат одного запроса помещается во временную таблицу и далее используется в следующих запросах пакета.
Типичные ситуации, где пакетный запрос оправдан: отбор ссылок по сложному условию с последующим соединением с регистрами, расчёт итогов с предварительной фильтрацией данных, формирование отчётов с несколькими уровнями детализации, получение сразу нескольких независимых выборок для одного алгоритма.
Если задача — получить одну простую выборку, пакет не нужен: он усложнит отладку без выигрыша в производительности. Пакет оправдан там, где без него пришлось бы создавать несколько объектов «Запрос» и передавать промежуточные данные через таблицы значений или параметры-массивы.
Синтаксис пакетного запроса
Каждый запрос в пакете отделяется от следующего точкой с запятой. Запрос, создающий временную таблицу, содержит конструкцию ПОМЕСТИТЬ ИмяТаблицы перед предложением ИЗ. Последний запрос пакета обычно не помещает результат во временную таблицу — это итоговая выборка, которую получит программа.
Пример пакета из трёх запросов:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| РеализацияТовары.Ссылка КАК Документ,
| РеализацияТовары.Номенклатура КАК Номенклатура,
| СУММА(РеализацияТовары.Количество) КАК Количество
|ПОМЕСТИТЬ ВТ_Отгрузки
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТовары
|ГДЕ
| РеализацияТовары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|
|СГРУППИРОВАТЬ ПО
| РеализацияТовары.Ссылка,
| РеализацияТовары.Номенклатура
|;
|
|ВЫБРАТЬ
| ВТ_Отгрузки.Номенклатура КАК Номенклатура,
| СУММА(ВТ_Отгрузки.Количество) КАК ВсегоОтгружено
|ПОМЕСТИТЬ ВТ_Итоги
|ИЗ
| ВТ_Отгрузки КАК ВТ_Отгрузки
|
|СГРУППИРОВАТЬ ПО
| ВТ_Отгрузки.Номенклатура
|;
|
|ВЫБРАТЬ
| ВТ_Итоги.Номенклатура КАК Номенклатура,
| ВТ_Итоги.ВсегоОтгружено КАК ВсегоОтгружено,
| Цены.Цена КАК Цена
|ИЗ
| ВТ_Итоги КАК ВТ_Итоги
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ) КАК Цены
| ПО ВТ_Итоги.Номенклатура = Цены.Номенклатура";
Все параметры (&НачалоПериода, &КонецПериода, &Период) устанавливаются один раз через УстановитьПараметр() — они видны всем запросам пакета. Это избавляет от необходимости дублировать установку параметров для каждой части.
Как выполнить пакет и получить каждый результат
Для выполнения пакетного запроса используется метод ВыполнитьПакет(). Он возвращает массив, где каждый элемент — результат соответствующего запроса пакета (объект «РезультатЗапроса»). Индексация в массиве идёт с нуля и соответствует порядку запросов в тексте.
МассивРезультатов = Запрос.ВыполнитьПакет();
// МассивРезультатов[0] — результат первого запроса (ПОМЕСТИТЬ ВТ_Отгрузки)
// МассивРезультатов[1] — результат второго запроса (ПОМЕСТИТЬ ВТ_Итоги)
// МассивРезультатов[2] — итоговая выборка
Выборка = МассивРезультатов[2].Выбрать();
Пока Выборка.Следующий() Цикл
// обработка строки
КонецЦикла;
Если нужен только последний результат пакета, можно использовать привычный метод Выполнить() — он вернёт результат последнего запроса в пакете. Это самый распространённый сценарий: промежуточные запросы только строят временные таблицы, а обрабатывается итоговая выборка.
Есть и третий вариант — ВыполнитьПакетСПромежуточнымиДанными(). Он возвращает массив, где для запросов с ПОМЕСТИТЬ результат содержит саму выборку временной таблицы, а не только количество помещённых записей. Это удобно для отладки: можно посмотреть содержимое каждой временной таблицы без отдельных запросов.
Способы выполнения пакета
Возвращает только результат последнего запроса. Подходит, когда промежуточные данные не нужны.
Возвращает массив результатов всех запросов. Для запросов с ПОМЕСТИТЬ результат содержит лишь служебную информацию о количестве записей.
Возвращает массив результатов, включая содержимое временных таблиц. Используется для отладки и анализа промежуточных выборок.
Работа с менеджером временных таблиц
Временные таблицы привязаны к менеджеру временных таблиц (объект «МенеджерВременныхТаблиц»). Если менеджер не задан явно, платформа создаёт его неявно для каждого запроса, и таблицы живут до конца выполнения пакета. Для большинства задач этого достаточно.
Явное создание менеджера нужно в двух случаях. Первый — когда одна и та же временная таблица используется в нескольких разных объектах «Запрос»: менеджер передаётся каждому из них через свойство МенеджерВременныхТаблиц. Второй — когда во временную таблицу нужно загрузить данные из таблицы значений, подготовленной в коде.
МенеджерВТ = Новый МенеджерВременныхТаблиц;
// Первый запрос: загружаем таблицу значений во временную таблицу
ЗапросЗагрузки = Новый Запрос;
ЗапросЗагрузки.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросЗагрузки.Текст =
"ВЫБРАТЬ
| Таб.Номенклатура КАК Номенклатура,
| Таб.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Данные
|ИЗ
| &ТаблицаЗначений КАК Таб";
ЗапросЗагрузки.УстановитьПараметр("ТаблицаЗначений", ПодготовленнаяТЗ);
ЗапросЗагрузки.Выполнить();
// Второй запрос: используем ту же временную таблицу
ЗапросОсновной = Новый Запрос;
ЗапросОсновной.МенеджерВременныхТаблиц = МенеджерВТ;
ЗапросОсновной.Текст =
"ВЫБРАТЬ
| ВТ_Данные.Номенклатура КАК Номенклатура,
| Остатки.КоличествоОстаток КАК Остаток
|ИЗ
| ВТ_Данные КАК ВТ_Данные
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
| ПО ВТ_Данные.Номенклатура = Остатки.Номенклатура";
Результат = ЗапросОсновной.Выполнить();
После завершения работы менеджер временных таблиц рекомендуется закрывать методом Закрыть() — это освобождает ресурсы СУБД, особенно при работе с крупными таблицами в циклах.
Удаление временных таблиц и управление памятью
Большие временные таблицы занимают место в tempdb (в случае MS SQL Server) или аналогичном хранилище СУБД. Если в длинном пакете промежуточная таблица больше не нужна, её стоит удалить явно — конструкцией УНИЧТОЖИТЬ внутри пакета.
Запрос.Текст =
"УНИЧТОЖИТЬ ВТ_Отгрузки
|;
|
|ВЫБРАТЬ
| ВТ_Итоги.Номенклатура КАК Номенклатура
|ИЗ
| ВТ_Итоги КАК ВТ_Итоги";
Уничтожение особенно актуально в длительных обработках и отчётах, где пакет выполняется многократно в цикле с одним менеджером временных таблиц. Без очистки объём временных данных растёт, и производительность деградирует от итерации к итерации.
Дополнительный приём для ускорения — создание индекса на временной таблице через конструкцию ИНДЕКСИРОВАТЬ ПО. Если временная таблица содержит десятки тысяч строк и дальше соединяется с большим регистром, индекс по полям соединения может сократить время выполнения в разы.
- 1Сформулируйте итоговую выборку и определите, какие промежуточные данные для неё нужны
- 2Напишите первый запрос с ПОМЕСТИТЬ для отбора исходных данных, добавьте ИНДЕКСИРОВАТЬ ПО при больших объёмах
- 3Добавьте промежуточные запросы, каждый со своей временной таблицей и уникальным именем
- 4Напишите финальный запрос, читающий временные таблицы и возвращающий результат
- 5Установите все параметры через УстановитьПараметр() до выполнения
- 6Выполните пакет методом Выполнить() или ВыполнитьПакет() и проверьте результат на тестовых данных
- 7Добавьте УНИЧТОЖИТЬ для крупных временных таблиц, которые не нужны в конце пакета
Типичные ошибки при работе с пакетами
Самая частая ошибка — обращение к временной таблице, которая ещё не создана или уже уничтожена. Порядок запросов в пакете строго последовательный: нельзя в третьем запросе сослаться на таблицу, которая создаётся в четвёртом. Платформа выдаст ошибку «Таблица не найдена».
Вторая группа ошибок связана с индексами результатов пакета. Разработчик добавляет в начало пакета ещё один запрос, а код обращения к МассивРезультатов[2] остаётся прежним — и программа молча обрабатывает не ту выборку. Надёжнее вычислять индекс последнего результата как МассивРезультатов.ВГраница() или использовать Выполнить() для итоговой выборки.
Третья типичная проблема — неявное создание менеджера временных таблиц при попытке использовать одну временную таблицу в двух объектах «Запрос». Без общего менеджера второй запрос не увидит таблицу первого, и разработчик получает ошибку, хотя «вчера всё работало» — просто раньше оба запроса были в одном пакете.
Проверка результата и отладка пакета
Отлаживать пакет удобнее всего по частям. Консоль запросов (типовая из инструментов разработчика или внешняя обработка) позволяет выполнить пакет целиком и посмотреть содержимое каждой временной таблицы. Если консоли под рукой нет, используйте ВыполнитьПакетСПромежуточнымиДанными() и выгрузите промежуточные результаты в таблицы значений для просмотра.
Полезный приём — временно заменить ПОМЕСТИТЬ на обычную выборку в подозрительном запросе и выполнить его отдельно. Так быстро проверяется, верные ли данные попадают во временную таблицу, прежде чем искать ошибку в соединениях финального запроса.
Контрольные точки для проверки корректности пакета:
Как проверить результат
Совпадает с ожидаемым числом отобранных строк по условию
Ноль строк или в разы больше ожидаемого — ошибка в условии или в соединении (декартово произведение)
Сходятся с контрольным отчётом или расчётом вручную по выборке документов
Суммы завышены кратно числу строк соединяемой таблицы — дублирование при соединении
Стабильное на повторных запусках и адекватное объёму данных
Растёт от запуска к запуску — вероятно, не очищаются временные таблицы
Когда пакетный запрос не поможет
Пакет не ускоряет выборку сам по себе — он лишь сокращает число обращений к СУБД. Если узкое место — отсутствие индексов, неоптимальные соединения или выборка миллионов строк без отбора, объединение запросов в пакет эффекта не даст. Сначала оптимизируется каждый запрос по отдельности, потом они объединяются.
Также пакет не подходит, когда между запросами нужна логика на встроенном языке: ветвления, обращения к объектам, вычисления. В этом случае либо логику переносят в запросы (условия, соединения), либо остаются отдельные запросы с общим менеджером временных таблиц — так сохраняется и гибкость кода, и выигрыш от временных таблиц.
Как выбрать подход
Что важно запомнить
Пакетный запрос — это несколько запросов в одном тексте, разделённых точкой с запятой, с обменом данными через временные таблицы. Выполняется методом ВыполнитьПакет() (массив результатов) или Выполнить() (только итог). Следите за порядком запросов, уникальностью имён временных таблиц, индексами результатов и своевременным уничтожением крупных временных таблиц.
Частые вопросы о пакетных запросах в 1С
Можно ли в пакетном запросе использовать обращение к временной таблице из другого запроса?
Да, но только если оба запроса работают с одним менеджером временных таблиц. Внутри одного пакета это происходит автоматически. Для двух разных объектов «Запрос» нужно явно создать МенеджерВременныхТаблиц и присвоить его свойству МенеджерВременныхТаблиц каждого запроса.
Почему ВыполнитьПакет() возвращает пустые результаты для запросов с ПОМЕСТИТЬ?
Это штатное поведение: для запросов, помещающих данные во временную таблицу, результат содержит только служебную информацию (например, количество помещённых записей), а не саму выборку. Чтобы получить содержимое временных таблиц, используйте ВыполнитьПакетСПромежуточнымиДанными().
Как узнать, сколько записей попало во временную таблицу?
Проще всего — через результат соответствующего элемента массива ВыполнитьПакет(): у результата запроса с ПОМЕСТИТЬ можно вызвать Выбрать() и получить единственную строку с количеством записей в некоторых версиях платформы. Универсальный способ — добавить в пакет отдельный запрос ВЫБРАТЬ КОЛИЧЕСТВО(*) из временной таблицы.
Что быстрее: пакетный запрос или несколько отдельных запросов?
При прочих равных пакет быстрее за счёт меньшего числа обращений к СУБД — особенно в клиент-серверном режиме. Но если каждый запрос по отдельности неоптимален, пакет не поможет: сначала нужно настроить отборы, соединения и индексы. Реальную разницу стоит измерять замером производительности на рабочих объёмах данных.
Нужно ли удалять временные таблицы, если пакет выполняется один раз?
Для однократного выполнения с неявным менеджером временных таблиц явное удаление не обязательно — таблицы уничтожатся автоматически. УНИЧТОЖИТЬ имеет смысл внутри длинного пакета, когда крупная таблица больше не нужна, и в циклах с общим менеджером, где временные данные накапливаются.
Можно ли отладить пакетный запрос в консоли запросов?
Да, большинство консолей запросов поддерживают пакетные запросы и показывают результат каждого запроса пакета, включая содержимое временных таблиц. Это самый удобный способ отладки: текст переносится из модуля в консоль, параметры заполняются вручную, и становится видно, на каком шаге данные расходятся с ожиданиями.