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

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

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

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

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

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

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

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

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

Пример — выбрать товары, цена которых выше средней по всему справочнику:

ВЫБРАТЬ

Товары.Ссылка,

Товары.Цена

ИЗ

Справочник.Товары КАК Товары

ГДЕ

Товары.Цена > (ВЫБРАТЬ

СРЕДНЕЕ(Т.Цена)

ИЗ

Справочник.Товары КАК Т)

Здесь подзапрос вычисляет одно скалярное значение, которое подставляется в условие. Другой вариант — подзапрос как источник: сначала группируем продажи по контрагентам, потом соединяем результат со справочником, чтобы получить реквизиты клиентов. Оба приёма работают в консоли запросов, в СКД и в программном коде через объект Запрос.

Основные сценарии, где подзапрос реально упрощает работу

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

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

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

Отбор по агрегатуСРЕДНЕЕ, МАКСИМУМ, СУММА

Нужно сравнить строку с вычисленным показателем по выборке

Проверка вхожденияВ / НЕ В

Требуется отобрать записи по списку, полученному другой выборкой

Группировка до соединенияСГРУППИРОВАТЬ ПО внутри

Соединять нужно уже свёрнутые данные, а не сырые строки

Один запрос вместо двухКомпактный код

Нет желания передавать промежуточные результаты через параметры

Как выбрать между подзапросом, временной таблицей и виртуальной таблицей

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

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

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

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

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

Короткая логика, одноразовое использование результата, небольшие объёмы. Ограничение: нельзя проиндексировать, сложнее отлаживать.

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

Результат используется многократно или объём велик. Можно индексировать. Ограничение: длиннее текст пакетного запроса.

Виртуальная таблица регистра

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

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

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

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

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

Пошаговая проверка и отладка запроса с подзапросом

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

Затем проверьте типы и состав полей: если подзапрос используется в условии В, он должен возвращать ровно одно поле, и его тип должен совпадать с типом сравниваемого значения. Несовпадение типов (например, составное поле против конкретного справочника) приводит к неявным преобразованиям и потере индекса.

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

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

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

Самая распространённая ошибка — забыть псевдоним у подзапроса в секции ИЗ. Платформа требует, чтобы любой источник имел имя; без псевдонима запрос просто не выполнится. Менее очевидная ошибка — обращение к полям подзапроса без его псевдонима, что при нескольких источниках вызывает неоднозначность.

Вторая группа ошибок — логические: подзапрос возвращает несколько значений там, где ожидается скаляр (например, в сравнении «=» вместо «В»). Результат зависит от режима СУБД: где-то будет ошибка выполнения, где-то — непредсказуемое сравнение с первой строкой. Всегда используйте В для списков и гарантируйте единственность для скаляров через МАКСИМУМ или РАЗЛИЧНЫЕ с группировкой.

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

Типичные ошибки
01
Подзапрос без псевдонима в секции ИЗ — запрос не выполняется
02
Сравнение «=» с подзапросом, возвращающим несколько строк, — ошибка или неверный результат; используйте В
03
Скалярный подзапрос в списке полей по большому регистру — резкое падение производительности
04
Отсутствие отбора по периоду внутри подзапроса — полное сканирование таблицы
05
Копирование одного подзапроса в несколько мест вместо временной таблицы — риск рассинхронизации логики

Ограничения синтаксиса и границы применимости

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

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

В СКД вложенные запросы работают, но затрудняют использование конструктора и автоматическую оптимизацию схемы. Если отчёт строится на СКД и подзапрос становится громоздким, правильнее вынести подготовку данных в набор данных-запрос с временными таблицами или в объединение наборов.

Проверка результата: как убедиться, что запрос работает правильно

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

Проверьте граничные случаи: записи с пустыми значениями (NULL в соединениях), документы без движений, контрагенты без заказов. Именно на таких строках подзапросы с НЕ В и левыми соединениями чаще всего дают расхождения.

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

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

Итоговые суммы по выборке
Норма

Совпадают с типовым отчётом или контрольной выборкой

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

Расхождение — проверять соединения и группировку

Строки с пустыми значениями
Норма

NULL обрабатываются явно, строки не теряются

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

Записи без связанных данных пропадают из результата

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

Укладывается в приемлемое для пользователей время

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

Резкий рост времени при увеличении объёма данных

Повторный запуск с теми же параметрами
Норма

Результат стабилен

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

Результат меняется — вероятна неоднозначность в подзапросе

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

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

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

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

Можно ли использовать вложенный запрос в СКД?

Да, набор данных типа «Запрос» в СКД поддерживает вложенные запросы — они пишутся в тексте запроса обычным образом. Однако сложные подзапросы затрудняют работу конструктора и настройку полей. Если логика разрастается, лучше оформить подготовку данных через временные таблицы в том же тексте запроса или вынести часть логики в набор данных-объект.

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

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

Почему подзапрос в условии В работает медленно?

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

Поддерживаются ли коррелированные подзапросы в 1С?

Нет. Вложенный запрос не может обращаться к полям внешнего запроса — он всегда выполняется независимо. Связь между уровнями задаётся только через соединения и условия во внешнем запросе. Если нужна логика «для каждой строки вычислить по её значению», используйте соединение с группировкой.

Сколько уровней вложенности допустимо?

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

Когда подзапрос лучше заменить на соединение?

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

Итог

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

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