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

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

Вложенный запрос в 1С: для чего нужен и как его использовать

Разбираем, зачем нужны вложенные запросы в 1С, когда их применять, синтаксис, примеры кода, типичные ошибки и альтернативы — пакетные запросы и временные таблицы.

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

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

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

Что такое вложенный запрос и как он работает

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

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

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

Основные сценарии применения вложенных запросов

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

Второй сценарий — отбор по условию «входит / не входит». Вложенный запрос формирует список значений (например, номенклатуру с движениями за период), а внешний фильтрует основную выборку через конструкцию «В» или «НЕ В». Это заменяет цикл с проверками в коде.

Третий сценарий — соединение с уже отфильтрованными данными. Вместо соединения с полной таблицей регистра и последующего отбора внешний запрос соединяется сразу с усечённым результатом вложенного. Это уменьшает объём обрабатываемых данных и упрощает логику.

Пример: остатки только по проданным товарам

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

bsl
ВЫБРАТЬ
	Остатки.Номенклатура КАК Номенклатура,
	Остатки.Склад КАК Склад,
	Остатки.КоличествоОстаток КАК Остаток
ИЗ
	РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК Остатки
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
			ВЫБРАТЬ РАЗЛИЧНЫЕ
				Продажи.Номенклатура КАК Номенклатура
			ИЗ
				РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК Продажи
		) КАК ПроданныеТовары
		ПО Остатки.Номенклатура = ПроданныеТовары.Номенклатура

Обратите внимание на псевдоним «ПроданныеТовары» после закрывающей скобки вложенного запроса — без него конструкция не скомпилируется. Псевдоним обязателен для любого вложенного источника.

Отбор через конструкцию «В»

Альтернативная форма той же задачи — фильтр в секции «ГДЕ»: «Номенклатура В (ВЫБРАТЬ ...)». Такой вариант короче, когда соединение не нужно и достаточно проверки вхождения. Для проверки отсутствия данных используется «НЕ В», но с ним связана важная особенность, описанная ниже в разделе про ошибки.

Синтаксис и правила оформления

Вложенный запрос записывается в круглых скобках и подчиняется тем же правилам, что и обычный запрос: секции «ВЫБРАТЬ», «ИЗ», «ГДЕ», «СГРУППИРОВАТЬ ПО» доступны в полном объёме. Внутри можно использовать параметры внешнего запроса — они видны на всех уровнях вложенности.

Обязательные правила: каждый вложенный запрос-источник должен иметь псевдоним; поля вложенного запроса адресуются через этот псевдоним; секция «УПОРЯДОЧИТЬ ПО» во вложенном запросе обычно бессмысленна, так как порядок промежуточного результата не гарантируется и не влияет на итоговую выборку (исключение — сочетание с «ПЕРВЫЕ n»).

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

Производительность: когда вложенный запрос ускоряет, а когда тормозит

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

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

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

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

Скорость выполнения запроса
Норма

Запрос с вложенным источником выполняется не медленнее варианта с временной таблицей

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

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

Состав результата
Норма

Количество строк и итоги совпадают с контрольным расчётом на тестовых данных

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

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

Условия отбора
Норма

Фильтры по периоду и измерениям стоят внутри вложенного запроса

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

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

На файловых базах разница между подходами менее заметна, но на клиент-серверных вариантах с большими таблицами выбор конструкции напрямую влияет на время формирования отчётов. Проверять стоит на данных, сопоставимых по объёму с рабочей базой.

Альтернативы: временные таблицы и пакетные запросы

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

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

Сравнение подходов

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

Подходит для компактной логики в 1-2 уровня. Ограничение: сложно отладить промежуточный результат, глубокая вложенность трудно читается.

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

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

Несколько отдельных запросов

Подходят, когда промежуточный результат нужно обработать кодом. Ограничение: лишние обращения к СУБД и передача больших выборок в память.

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

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

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

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

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

Самая коварная ошибка — использование «НЕ В» с вложенным запросом, который может вернуть NULL. Если хотя бы одно значение в списке равно NULL, условие «НЕ В» не сработает ни для одной строки, и выборка окажется пустой без видимой причины. Защита — отбор «ГДЕ Поле ЕСТЬ НЕ NULL» внутри вложенного запроса или замена конструкции на соединение с проверкой «ЕСТЬ NULL».

Вторая частая ошибка — дублирование строк после соединения. Если вложенный запрос не сгруппирован до уникальности ключа соединения, внешний запрос перемножит строки. Признак — завышенные итоги по суммам и количествам. Лечится добавлением «СГРУППИРОВАТЬ ПО» или «РАЗЛИЧНЫЕ» во вложенный запрос.

Типичные ошибки
01
НЕ В с возможным NULL во вложенном запросе — внешняя выборка становится пустой без ошибки выполнения
02
Отсутствие псевдонима у вложенного источника — запрос не проходит проверку синтаксиса
03
Соединение с несгруппированным вложенным запросом — дубли строк и завышенные итоги
04
Отбор только во внешнем запросе — СУБД обрабатывает лишние миллионы строк внутри
05
Ожидание порядка строк из вложенного запроса — сортировку нужно задавать во внешнем запросе

Также помните об ограничении: вложенный запрос не может обращаться к полям внешнего запроса (коррелированные подзапросы в языке 1С не поддерживаются в том виде, как в чистом SQL). Связь между уровнями осуществляется только через соединение по полям результата или через общие параметры.

Порядок разработки запроса с вложенным источником

Работать удобнее всего итеративно, от простого к сложному. Сначала пишется и проверяется внутренний запрос как самостоятельный, затем он встраивается во внешний, после чего добавляются соединения и итоговые условия.

Порядок действий
  1. 1
    Сформулируйте, какой промежуточный результат нужен: отбор, агрегация или список значений
  2. 2
    Напишите внутренний запрос отдельно и выполните его в консоли запросов на тестовых данных
  3. 3
    Проверьте уникальность ключей соединения и отсутствие NULL в полях фильтрации
  4. 4
    Встройте запрос во внешний, задайте псевдоним и пропишите условие соединения
  5. 5
    Перенесите максимум условий отбора внутрь вложенного запроса
  6. 6
    Сверьте итоги с контрольным расчётом и сравните время выполнения с альтернативным вариантом

После внедрения полезно вернуться к запросу через некоторое время эксплуатации: с ростом объёма данных план выполнения может измениться, и конструкция, бывшая быстрой на старте, начнёт отрабатывать медленнее. Это сигнал пересмотреть структуру — возможно, перенести часть логики во временные таблицы.

При переносе запроса из консоли в модуль не забывайте, что текст запроса в коде — это строка: двойные кавычки внутри неё нужно удваивать. Удобно формировать текст через многострочные строковые литералы с вертикальной чертой, чтобы сохранить форматирование и читаемость.
ПЗ
Практическое замечание

Частые вопросы о вложенных запросах в 1С

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

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

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

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

Почему запрос с «НЕ В» возвращает пустой результат?

Наиболее вероятная причина — наличие NULL среди значений, которые возвращает вложенный запрос. В логике SQL сравнение с NULL даёт неопределённость, и условие «НЕ В» перестаёт выполняться для всех строк. Добавьте во вложенный запрос отбор «ГДЕ Поле ЕСТЬ НЕ NULL».

Есть ли ограничение на глубину вложенности?

Жёсткого практического ограничения со стороны платформы нет, но запросы с тремя и более уровнями становятся трудными для чтения и отладки. В таких случаях рекомендуется переходить на пакетные запросы с временными таблицами.

Работает ли сортировка внутри вложенного запроса?

Секция «УПОРЯДОЧИТЬ ПО» во вложенном запросе имеет смысл только вместе с «ПЕРВЫЕ n» — для отбора, например, последних документов. Итоговый порядок строк всегда определяется сортировкой внешнего запроса, поэтому рассчитывать на порядок промежуточного результата нельзя.

Можно ли во вложенном запросе обратиться к полю внешнего?

Нет, коррелированные подзапросы в языке запросов 1С не поддерживаются. Связь между уровнями строится через соединение результата вложенного запроса по полям либо через общие параметры запроса.

Итог

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

Вложенный запрос в 1С позволяет использовать результат одного запроса как источник или условие в другом, выполняя многошаговую логику одним обращением к базе. Он уместен для простой агрегации и отбора, требует псевдонима, аккуратности с NULL и группировкой, а при усложнении логики уступает место временным таблицам и пакетным запросам.