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

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

1С: запрос как параметр запроса — вложенные запросы и временные таблицы

Как передать результат одного запроса в другой в 1С: вложенные запросы, временные таблицы, параметры-таблицы значений. Примеры кода, ошибки и проверка результата.

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

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

В материале разобраны синтаксис вложенных запросов, работа с МенеджерВременныхТаблиц, передача таблицы значений параметром, типичные ошибки и способы проверить, что запрос отрабатывает так, как задумано.

Как устроен вложенный запрос в тексте запроса

Самый простой способ использовать один запрос внутри другого — вложенный (подчинённый) запрос. Он записывается в круглых скобках в предложении ИЗ, в условии ГДЕ или в списке выборки. Платформа выполняет его как часть общего запроса, и с точки зрения СУБД это один запрос, а не два отдельных обращения.

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

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

"ВЫБРАТЬ

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

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

|ИЗ

| (ВЫБРАТЬ

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

| ПродажиОбороты.КоличествоОборот КАК Количество

| ИЗ

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

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

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

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

Ограничение подхода — читаемость и отладка. Три-четыре уровня вложенности делают текст трудным для анализа, а план запроса на СУБД может оказаться хуже, чем у варианта с временными таблицами, потому что оптимизатор вынужден обрабатывать всё выражение целиком.

Временные таблицы: запрос как параметр через МенеджерВременныхТаблиц

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

Порядок действий с временной таблицей
  1. 1
    Создайте МенеджерВременныхТаблиц и назначьте его первому запросу
  2. 2
    В тексте первого запроса используйте ПОМЕСТИТЬ ИмяТаблицы и выполните запрос
  3. 3
    Создайте второй запрос и назначьте ему тот же менеджер
  4. 4
    Обратитесь к временной таблице по имени в предложении ИЗ
  5. 5
    После использования уничтожьте таблицу командой УНИЧТОЖИТЬ или закройте менеджер
МенеджерВТ = Новый МенеджерВременныхТаблиц;

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

Запрос1.МенеджерВременныхТаблиц = МенеджерВТ;

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

"ВЫБРАТЬ

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

|ПОМЕСТИТЬ ВТ_Отбор

|ИЗ

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

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

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

|ИМЕЮЩИЕ СУММА(Продажи.КоличествоОборот) > 100";

Запрос1.УстановитьПараметр("Начало", НачалоПериода);

Запрос1.УстановитьПараметр("Конец", КонецПериода);

Запрос1.Выполнить();

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

Запрос2.МенеджерВременныхТаблиц = МенеджерВТ;

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

"ВЫБРАТЬ

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

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

|ИЗ

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

| Номенклатура В (ВЫБРАТЬ Т.Номенклатура ИЗ ВТ_Отбор КАК Т)) КАК Остатки";

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

Ключевой момент: если не назначить второму запросу тот же менеджер, обращение к ВТ_Отбор вызовет ошибку «Таблица не найдена». Временная таблица живёт внутри своего менеджера и недоступна остальным запросам.

Передача таблицы значений параметром запроса

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

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

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

"ВЫБРАТЬ

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

| ТЗ.Количество КАК Количество

|ПОМЕСТИТЬ ВТ_Данные

|ИЗ

| &ТаблицаДанных КАК ТЗ

|;

|ВЫБРАТЬ

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

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

|ИЗ

| РегистрСведений.ЦеныНоменклатуры.СрезПоследних(,

| Номенклатура В (ВЫБРАТЬ Т.Номенклатура ИЗ ВТ_Данные КАК Т)) КАК Цены";

Запрос.УстановитьПараметр("ТаблицаДанных", ТаблицаЗначений);

Требования к таблице значений: колонки должны иметь типизированные значения, а имена колонок — соответствовать правилам именования полей запроса. Нетипизированные колонки (составной тип без ограничения) могут привести к ошибке СУБД или неявным преобразованиям. Перед передачей большой таблицы имеет смысл свернуть её и оставить только нужные колонки — вся таблица целиком передаётся на сервер СУБД.

Сравнение способов и критерии выбора

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

Сравнение вариантов

Вложенный запрос

Подходит для одноразовой фильтрации и группировки внутри одного запроса; ограничение — плохая читаемость при глубокой вложенности

Временная таблица

Оптимальна при многократном использовании промежуточного результата и пошаговой отладке; требует общего МенеджерВременныхТаблиц

Таблица значений параметром

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

КритерийВложенный запросВременная таблицаТаблица значений параметром
Источник данныхБаза данныхБаза данныхКод / внешние данные
Повторное использованиеНетДа, в рамках менеджераДа, через повторную передачу
Отладка промежуточного результатаСложноПросто — выборкой из ВТПросто — до выполнения запроса
ИндексацияНа усмотрение СУБДИНДЕКСИРОВАТЬ ПОИНДЕКСИРОВАТЬ ПО после ПОМЕСТИТЬ

Практическое правило: если промежуточный результат нужен два и более раза или запрос становится нечитаемым — переходите на временные таблицы. Если данные рождаются вне запроса — только параметр с таблицей значений.

Пакетные запросы и отладка промежуточных результатов

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

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

"ВЫБРАТЬ Т.Номенклатура ПОМЕСТИТЬ ВТ_Отбор ИЗ &ТЗ КАК Т

|;

|ВЫБРАТЬ * ИЗ ВТ_Отбор КАК ВТ_Отбор"; // контрольная выборка

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

ПромежуточныеДанные = МассивРезультатов[1].Выгрузить();

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

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

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

Выборка из ВТ возвращает ожидаемое число строк и корректные значения

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

Пустая выборка или дубли строк — ошибка в условиях первого запроса

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

Число строк результата соответствует логике соединения

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

Результат раздувается — в ВТ есть дубли ключевых полей

Время выполнения
Норма

Запрос с ВТ выполняется не медленнее вложенного варианта

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

Резкая деградация — отсутствует ИНДЕКСИРОВАТЬ ПО по полям соединения

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

Типичные ошибки и ограничения

Типичные ошибки
01
Разные менеджеры временных таблиц у запросов — вторая выборка падает с ошибкой «Таблица не найдена»
02
Передача нетипизированной таблицы значений параметром — ошибка СУБД о невозможности определить тип колонки
03
Соединение с ВТ без ИНДЕКСИРОВАТЬ ПО на больших объёмах — полное сканирование и тайм-ауты
04
Обращение к временной таблице после закрытия менеджера или в другом сеансе — таблица уже не существует
05
Дубли строк в промежуточной таблице без СГРУППИРОВАТЬ ПО — размножение строк при соединении и неверные итоги

Отдельно стоит упомянуть параметры виртуальных таблиц. Условие отбора лучше накладывать в параметрах виртуальной таблицы (например, Остатки(, Номенклатура В (...))), а не в секции ГДЕ готовой выборки — тогда СУБД отбирает данные до соединения, что существенно быстрее на больших регистрах.

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

Можно ли передать объект «Запрос» как параметр другого запроса?

Нет, параметром можно передать только данные: примитивные типы, ссылки, таблицы значений, моменты времени. Результат «запроса в запросе» передаётся либо текстом вложенного запроса, либо через временную таблицу, либо выгрузкой в таблицу значений.

Почему второй запрос не видит временную таблицу первого?

Потому что временные таблицы существуют только внутри своего МенеджерВременныхТаблиц. Назначьте обоим запросам один и тот же экземпляр менеджера через свойство МенеджерВременныхТаблиц.

Что быстрее: вложенный запрос или временная таблица?

Однозначного ответа нет: на малых объёмах разница незаметна. На больших выборках временная таблица с ИНДЕКСИРОВАТЬ ПО часто выигрывает, потому что индексируется и материализуется один раз. Проверять нужно замером на реальных данных.

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

Передайте список значений или массив через УстановитьПараметр и используйте условие «Поле В (&Список)». Платформа автоматически развернёт коллекцию в перечисление значений. Это не требует создания временной таблицы.

Можно ли использовать временную таблицу в другом сеансе или фоновом задании?

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

Как посмотреть, что лежит во временной таблице, во время отладки?

Выполните пакетный запрос с контрольной выборкой «ВЫБРАТЬ * ИЗ ВТ_Имя» или используйте консоль запросов, поддерживающую просмотр временных таблиц менеджера.

Итог

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

Результат одного запроса передаётся в другой тремя способами: вложенным запросом в тексте, временной таблицей через общий МенеджерВременныхТаблиц и таблицей значений через УстановитьПараметр. Для повторного использования и отладки выбирайте временные таблицы, не забывайте про ИНДЕКСИРОВАТЬ ПО и проверяйте промежуточные данные контрольной выборкой.