К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как сделать второй запрос в 1С: пакетные запросы и временные таблицы

Разбираем, как выполнить второй запрос в 1С: пакетные запросы через точку с запятой, временные таблицы, несколько объектов Запрос. Примеры кода и типичные ошибки.

В языке запросов 1С «второй запрос» обычно означает одну из трёх задач: выполнить несколько выборок в одном пакетном запросе, сохранить промежуточный результат во временную таблицу и использовать его дальше, либо последовательно выполнить два разных объекта Запрос в коде. Все три сценария рабочие, но выбираются по-разному в зависимости от того, нужна ли связь между выборками и где обрабатывается результат.

Самый частый приём — пакетный запрос: несколько операторов ВЫБРАТЬ в одном тексте, разделённые точкой с запятой. Платформа выполняет их за одно обращение к СУБД и возвращает массив результатов, где индекс каждого элемента соответствует порядковому номеру запроса в пакете. Это быстрее, чем несколько отдельных обращений к базе, и позволяет передавать данные между запросами через временные таблицы.

Ниже разобраны все способы с примерами кода, правилами нумерации результатов, типичными ошибками и критериями выбора между пакетом, временными таблицами и отдельными запросами.

Что такое пакетный запрос и когда он нужен

Пакетный запрос — это один текст запроса, содержащий несколько операторов выборки, разделённых символом «;». Платформа компилирует и выполняет его целиком, а метод ВыполнитьПакет() возвращает массив, где каждый элемент — результат соответствующего запроса пакета.

Пакет оправдан, когда выборки логически связаны: например, сначала формируется временная таблица отборных документов, затем из неё считаются итоги, а третьим запросом выбираются детальные строки. Выполнение идёт в рамках одного соединения с СУБД, что сокращает накладные расходы по сравнению с серией отдельных запросов.

Если выборки независимы и результат второй не опирается на данные первой, пакет тоже допустим, но выигрыш меньше. А если между запросами требуется сложная логика на встроенном языке — ветвления, расчёты, обращения к объектам — корректнее выполнить два отдельных запроса в коде.

Как написать два запроса в одном пакете

Синтаксис прост: первый оператор ВЫБРАТЬ завершается точкой с запятой, после неё начинается второй. В модуле это выглядит так:

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

| Товары.Ссылка КАК Номенклатура

|ИЗ

| Справочник.Номенклатура КАК Товары

|ГДЕ

| НЕ Товары.ПометкаУдаления

|;

|

|ВЫБРАТЬ

| Остатки.Номенклатура КАК Номенклатура,

| Остатки.КоличествоОстаток КАК Остаток

|ИЗ

| РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки";

Результаты = Запрос.ВыполнитьПакет();

Товары = Результаты[0].Выгрузить();

Остатки = Результаты[1].Выгрузить();

Ключевой момент — нумерация результатов. Индекс массива соответствует порядку запросов в тексте, начиная с нуля. Если в пакете есть запросы, создающие временные таблицы (с конструкцией ПОМЕСТИТЬ), они тоже занимают позицию в массиве, хотя их результат не содержит полезных данных для выгрузки.

Метод Выполнить() для пакетного текста не подходит — он вернёт результат только последнего запроса. Для пакета всегда используйте ВыполнитьПакет() или ВыполнитьПакетСПромежуточнымиДанными(), если нужно посмотреть содержимое временных таблиц при отладке.

bsl
Результаты = Запрос.ВыполнитьПакет();
ТаблицаТоваров = Результаты[0].Выгрузить();
ТаблицаОстатков = Результаты[1].Выгрузить();

Второй запрос через временную таблицу

Временные таблицы — основной способ передать данные из первого запроса во второй внутри пакета. Первый оператор сохраняет выборку через ПОМЕСТИТЬ ИмяТаблицы, а второй обращается к ней как к обычной таблице.

Запрос.Текст =

"ВЫБРАТЬ

| Продажи.Номенклатура КАК Номенклатура,

| СУММА(Продажи.Количество) КАК Количество

|ПОМЕСТИТЬ ВТ_Продажи

|ИЗ

| РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи

|СГРУППИРОВАТЬ ПО

| Продажи.Номенклатура

|;

|

|ВЫБРАТЬ

| ВТ.Номенклатура КАК Номенклатура,

| ВТ.Количество КАК Продано,

| Цены.Цена КАК Цена

|ИЗ

| ВТ_Продажи КАК ВТ

| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Конец, ) КАК Цены

| ПО ВТ.Номенклатура = Цены.Номенклатура";

При такой структуре массив результатов содержит два элемента: Результаты[0] — служебный результат создания временной таблицы, Результаты[1] — итоговая выборка. Рабочие данные забираются именно из второго элемента.

Временная таблица живёт, пока существует менеджер временных таблиц запроса. Если объект Запрос создан без явного менеджера, платформа создаёт его неявно и уничтожает вместе с запросом. Для передачи временных таблиц между разными объектами запроса создайте МенеджерВременныхТаблиц явно и назначьте его обоим запросам.

Два отдельных запроса в коде модуля

Когда между выборками нужна логика на встроенном языке — проверка условий, вычисления, перебор результата первого запроса для формирования параметров второго — пакет не подходит. Выполните два независимых объекта Запрос последовательно.

// Первый запрос

ЗапросКлиенты = Новый Запрос;

ЗапросКлиенты.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.Контрагенты ГДЕ ЭтоГруппа = ЛОЖЬ";

ВыборкаКлиенты = ЗапросКлиенты.Выполнить().Выбрать();

Пока ВыборкаКлиенты.Следующий() Цикл

// Второй запрос выполняется в цикле с параметром

ЗапросДолг = Новый Запрос;

ЗапросДолг.Текст = "ВЫБРАТЬ СуммаОстаток ИЗ РегистрНакопления.Взаиморасчеты.Остатки(, Контрагент = &Контрагент)";

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

РезультатДолг = ЗапросДолг.Выполнить();

// обработка результата

КонецЦикла;

Такой подход проще отлаживать, но у него есть ограничение по производительности: запрос внутри цикла порождает обращение к СУБД на каждой итерации. При большом числе итераций это заметно медленнее пакета с соединением таблиц. Если записей много, перепишите логику на один запрос с объединением или временной таблицей.

У каждого объекта Запрос свой набор параметров. Параметры, установленные для первого запроса, не действуют на второй — их нужно устанавливать явно для каждого объекта.

Как выбрать подходящий способ

Выбор зависит от связанности выборок, объёма данных и места обработки результата. Ориентируйтесь на условия, а не на привычку.

СпособКогда подходитОграничение
Пакетный запрос с независимыми выборкамиНесколько несвязанных выборок, нужно сэкономить обращения к СУБДНельзя вставить логику на встроенном языке между запросами
Пакет с временной таблицейВторой выборке нужны отфильтрованные данные первойВТ живёт только в рамках менеджера временных таблиц
Два отдельных объекта ЗапросМежду запросами нужны ветвления, расчёты, переборБольше обращений к базе; опасен в циклах
Один запрос с соединениемДанные обеих выборок можно связать по ключуНе всегда выражает сложную многошаговую логику

Общее правило: если задачу можно выразить одним запросом с соединением или пакетом с временной таблицей — делайте так. Отдельные запросы в коде оставляйте для случаев, где действительно нужна процедурная логика между обращениями к базе.

Маршрут решения

Как выбрать действие

Второй выборке нужны данные первой выборкиИспользуйте пакет с временной таблицей через ПОМЕСТИТЬ
Выборки независимы и выполняются подрядОбъедините их в пакет и читайте результаты по индексам
Между запросами нужны расчёты или ветвления в кодеВыполните два отдельных объекта Запрос
Второй запрос вызывается в цикле по строкам первогоПерепишите на один запрос с соединением таблиц

Проверка результата и отладка

Первый шаг проверки — убедиться, что массив результатов имеет ожидаемую длину. Если ВыполнитьПакет() вернул меньше элементов, чем операторов в тексте, ищите синтаксическую ошибку или пропущенную точку с запятой — платформа сообщает о ней при выполнении, но при динамической сборке текста ошибку легко пропустить.

Для временных таблиц удобен метод ВыполнитьПакетСПромежуточнымиДанными(): он возвращает содержимое временных таблиц, что позволяет увидеть, что именно попало в промежуточную выборку. В консоли запросов (типовой или из инструментов разработчика) пакет тоже выполняется по частям — проверяйте каждый оператор отдельно перед сборкой.

Проверяйте результат каждого элемента массива на пустоту через Результаты[1].Пустой до выгрузки. Пустой результат второго запроса часто означает, что временная таблица оказалась пустой из-за неверных параметров отбора первого запроса, а не из-за ошибки во втором.

Контрольные точки

Как проверить результат

Длина массива результатов
Норма

Равна числу операторов ВЫБРАТЬ в тексте пакета

Тревожный признак

Меньше ожидаемого — ошибка в тексте или пропущен разделитель

Содержимое временной таблицы
Норма

ВТ содержит ожидаемые строки (проверка через ВыполнитьПакетСПромежуточнымиДанными)

Тревожный признак

ВТ пуста — неверные параметры отбора первого запроса

Результат второго запроса
Норма

Выборка содержит данные, типы полей соответствуют ожиданиям

Тревожный признак

Пусто или NULL в полях соединения — проверяйте условия связи

Типичные ошибки при работе со вторым запросом

Самая распространённая ошибка — неверный индекс результата. Разработчик забывает, что запрос с ПОМЕСТИТЬ занимает позицию в массиве, и выгружает Результаты[0] вместо Результаты[1]. Симптом — пустая или бессмысленная таблица на выходе без сообщения об ошибке.

Вторая ошибка — использование Выполнить() вместо ВыполнитьПакет(). В этом случае выполняется только последний запрос пакета, а временные таблицы могут не успеть корректно обработаться в ожидаемом контексте, что приводит к ошибке «Таблица не найдена».

Третья группа проблем — повторное использование имени временной таблицы без УНИЧТОЖИТЬ в одном менеджере, а также попытка обратиться к временной таблице из другого объекта запроса, которому не назначен общий менеджер временных таблиц.

Типичные ошибки
01
Неверный индекс результата: запрос с ПОМЕСТИТЬ сдвигает нумерацию, итоговая выборка оказывается в элементе с большим индексом
02
Вызов Выполнить() для пакетного текста: выполняется только последний запрос, промежуточные результаты теряются
03
Обращение к временной таблице из другого объекта Запрос без общего МенеджерВременныхТаблиц — ошибка «Таблица не найдена»
04
Второй запрос в цикле по большой выборке: сотни обращений к СУБД, рост времени выполнения и риск блокировок
05
Пропуск точки с запятой между операторами пакета — синтаксическая ошибка при выполнении

Частые вопросы

Как обратиться к результату второго запроса в пакете?

Метод ВыполнитьПакет() возвращает массив, где индекс элемента равен порядковому номеру запроса с нуля. Если в пакете два оператора ВЫБРАТЬ, результат второго — Результаты[1]. Если перед ним есть запрос с ПОМЕСТИТЬ, он тоже занимает позицию, и итоговая выборка смещается на следующий индекс.

Можно ли во втором запросе использовать параметры, отличные от первого?

Да. Параметры задаются методом УстановитьПараметр() один раз для всего объекта Запрос и доступны во всех операторах пакета. Каждый оператор может использовать свой набор параметров — главное, чтобы все они были установлены до выполнения.

Что быстрее: пакетный запрос или два отдельных?

Пакет обычно быстрее, потому что выполняется за одно обращение к серверу СУБД и позволяет оптимизатору учесть структуру всех операторов. Разница заметна на частых вызовах и больших объёмах. Для разовой операции с маленькими выборками разница практически не ощущается.

Как передать таблицу значений во второй запрос?

Через временную таблицу: первый оператор помещает таблицу значений в ВТ (источник — параметр с типом ТаблицаЗначений), второй соединяется с ней. Либо передайте таблицу параметром и используйте её в обоих запросах пакета.

Почему возникает ошибка «Таблица не найдена» при обращении к временной таблице?

Временная таблица существует только в рамках менеджера временных таблиц того запроса, который её создал. Если второй объект Запрос создаётся отдельно, назначьте обоим один МенеджерВременныхТаблиц. Внутри одного пакета эта ошибка означает, что оператор с ПОМЕСТИТЬ не выполнился — проверяйте порядок операторов и разделители.

Можно ли в пакете уничтожить временную таблицу?

Да, оператором УНИЧТОЖИТЬ ИмяТаблицы. Это освобождает память сервера, что важно при больших временных таблицах. Оператор уничтожения, как и остальные, занимает позицию в массиве результатов — учитывайте это при обращении по индексам.

Итог

Что важно запомнить

Второй запрос в 1С делается тремя способами: пакетом через точку с запятой с чтением результатов по индексам, временной таблицей через ПОМЕСТИТЬ для связанных выборок и отдельными объектами Запрос, когда между ними нужна логика в коде. Помните о сдвиге индексов из-за запросов с ПОМЕСТИТЬ и избегайте запросов в цикле по большим выборкам.