Разработка и Конфигурация · 13 мин
Зачем нужен вложенный запрос в 1С и когда его применять
Зачем нужен вложенный запрос в 1С: когда подзапрос упрощает выборку, а когда замедляет её. Примеры кода, альтернативы, типичные ошибки и проверка результата.
Вложенный запрос в языке запросов 1С — это запрос, заключённый в круглые скобки и используемый внутри другого запроса: как источник данных в секции ИЗ, как условие в секции ГДЕ или как выражение в списке выборки. По сути это способ разбить сложную выборку на два логических шага: сначала получить промежуточный набор данных, затем работать с ним как с обычной таблицей.
Типичная ситуация: нужно выбрать документы, по которым сумма продаж превышает среднюю по базе, или найти контрагентов, у которых нет ни одного заказа за период. Решить это одной «плоской» выборкой сложно — сначала требуется вычислить агрегат (среднее, максимум, список), и только потом использовать его как критерий отбора. Вложенный запрос позволяет сделать оба шага в одном тексте запроса без промежуточных таблиц значений и без обхода результата в коде.
При этом подзапрос — не универсальный инструмент. Во многих задачах его заменяют временные таблицы, виртуальные таблицы регистров или секция ИМЕЮЩИЕ, и эти альтернативы часто работают быстрее. Ниже разобраны реальные сценарии, критерии выбора и ограничения, которые помогут применять вложенные запросы осознанно.
Что такое вложенный запрос и как он выглядит в коде
Вложенный запрос записывается в круглых скобках и обязан иметь псевдоним, если используется как источник данных. Платформа выполняет его как самостоятельную выборку, а результат подставляет во внешний запрос. Синтаксически подзапрос почти не отличается от обычного: в нём доступны секции ВЫБРАТЬ, ИЗ, ГДЕ, СГРУППИРОВАТЬ ПО.
Пример — выбрать товары, цена которых выше средней по всему справочнику:
ВЫБРАТЬ
Товары.Ссылка,
Товары.Цена
ИЗ
Справочник.Товары КАК Товары
ГДЕ
Товары.Цена > (ВЫБРАТЬ
СРЕДНЕЕ(Т.Цена)
ИЗ
Справочник.Товары КАК Т)
Здесь подзапрос вычисляет одно скалярное значение, которое подставляется в условие. Другой вариант — подзапрос как источник: сначала группируем продажи по контрагентам, потом соединяем результат со справочником, чтобы получить реквизиты клиентов. Оба приёма работают в консоли запросов, в СКД и в программном коде через объект Запрос.
Основные сценарии, где подзапрос реально упрощает работу
Первый сценарий — отбор по агрегату: «документы с суммой выше средней», «последняя цена поставщика», «сотрудники с максимальным стажем». Без подзапроса пришлось бы выполнять два запроса из кода и передавать результат параметром, что увеличивает объём кода и усложняет сопровождение.
Второй сценарий — отбор по списку через конструкцию В. Например, найти контрагентов, у которых вообще не было продаж за период: подзапрос формирует список клиентов с движениями, а внешний запрос выбирает тех, кто в этот список НЕ входит. Такая логика лаконично записывается одним текстом.
Третий сценарий — предварительная группировка перед соединением. Если нужно соединить справочник с оборотами регистра, но обороты требуется сначала свернуть с отбором, подзапрос выполняет группировку, а внешний запрос соединяет уже компактный результат. Это уменьшает число строк, участвующих в соединении.
Нужно сравнить строку с вычисленным показателем по выборке
Требуется отобрать записи по списку, полученному другой выборкой
Соединять нужно уже свёрнутые данные, а не сырые строки
Нет желания передавать промежуточные результаты через параметры
Как выбрать между подзапросом, временной таблицей и виртуальной таблицей
Главная альтернатива вложенному запросу — пакетный запрос с временными таблицами. Разница принципиальна: временную таблицу платформа материализует и может проиндексировать, а подзапрос часто выполняется как часть единого плана СУБД и на больших объёмах может приводить к повторным вычислениям или неоптимальным соединениям.
Если промежуточный результат используется один раз и невелик — подзапрос короче и читабельнее. Если промежуточный результат нужен в нескольких местах запроса или содержит десятки тысяч строк — временная таблица почти всегда предпочтительнее: её можно поместить один раз, проиндексировать и соединять многократно.
Третий вариант — виртуальные таблицы регистров (Обороты, Остатки, СрезПоследних). Частая ошибка новичков — писать подзапрос с группировкой по физической таблице регистра там, где платформа уже предоставляет готовую виртуальную таблицу с нужной агрегацией. Она работает быстрее и корректнее с точки зрения итогов.
Сравнение подходов
Короткая логика, одноразовое использование результата, небольшие объёмы. Ограничение: нельзя проиндексировать, сложнее отлаживать.
Результат используется многократно или объём велик. Можно индексировать. Ограничение: длиннее текст пакетного запроса.
Нужны остатки, обороты или срез последних. Ограничение: применима только к регистрам, параметры виртуальной таблицы задавать обязательно.
Производительность: где подзапрос замедляет выборку
Подзапрос в секции ГДЕ, возвращающий список, может выполняться для каждой проверяемой строки, если СУБД не сумеет построить оптимальный план. На таблицах в сотни тысяч записей это превращается в заметную задержку. Признак проблемы — запрос «висит», хотя объём результата мал.
Подзапрос в списке выборки (скалярный подзапрос в поле) ещё опаснее: он потенциально выполняется для каждой строки результата. Если такой подзапрос обращается к регистру без отбора по индексированным полям, время выполнения растёт линейно от числа строк внешней выборки.
Практический критерий: если подзапрос можно вынести во временную таблицу без потери смысла — стоит попробовать оба варианта и сравнить время выполнения в консоли запросов на реальных данных. Разница в разы на больших базах — обычное дело.
Пошаговая проверка и отладка запроса с подзапросом
Отладка вложенного запроса начинается с изоляции: выполните подзапрос отдельно в консоли запросов и убедитесь, что он возвращает ожидаемые строки и не содержит лишних полей. Половина ошибок в подзапросах — неверные соединения или отсутствие отбора по периоду внутри, что внешний запрос никак не скомпенсирует.
Затем проверьте типы и состав полей: если подзапрос используется в условии В, он должен возвращать ровно одно поле, и его тип должен совпадать с типом сравниваемого значения. Несовпадение типов (например, составное поле против конкретного справочника) приводит к неявным преобразованиям и потере индекса.
Финальный шаг — замер времени на копии рабочей базы с реальным объёмом данных. Запрос, который на пустой тестовой базе выполняется мгновенно, на продуктивной базе может работать минутами.
- 1Выполните подзапрос отдельно в консоли запросов и сверьте результат с ожиданием
- 2Убедитесь, что внутри подзапроса заданы отборы по периоду и измерениям
- 3Проверьте, что для условия В подзапрос возвращает одно поле нужного типа
- 4Соберите полный запрос и проверьте корректность соединений и псевдонимов
- 5Замерьте время выполнения на копии базы с реальным объёмом данных
- 6При неудовлетворительном времени перепишите подзапрос как временную таблицу и сравните
Типичные ошибки при работе с вложенными запросами
Самая распространённая ошибка — забыть псевдоним у подзапроса в секции ИЗ. Платформа требует, чтобы любой источник имел имя; без псевдонима запрос просто не выполнится. Менее очевидная ошибка — обращение к полям подзапроса без его псевдонима, что при нескольких источниках вызывает неоднозначность.
Вторая группа ошибок — логические: подзапрос возвращает несколько значений там, где ожидается скаляр (например, в сравнении «=» вместо «В»). Результат зависит от режима СУБД: где-то будет ошибка выполнения, где-то — непредсказуемое сравнение с первой строкой. Всегда используйте В для списков и гарантируйте единственность для скаляров через МАКСИМУМ или РАЗЛИЧНЫЕ с группировкой.
Третья ошибка — дублирование логики: один и тот же подзапрос вставлен в запрос два-три раза. Это и признак того, что нужна временная таблица, и источник рассинхронизации при доработках: исправили один экземпляр, забыли другой.
Ограничения синтаксиса и границы применимости
Во вложенном запросе недоступны некоторые конструкции внешнего уровня: нельзя использовать секции ИТОГИ, УПОРЯДОЧИТЬ ПО имеет ограниченный смысл (порядок внутри подзапроса не гарантируется снаружи), а ПЕРВЫЕ работает, но только в сочетании с упорядочиванием внутри самого подзапроса. Также подзапрос не видит полей внешнего запроса — коррелированных подзапросов в классическом SQL-смысле в языке 1С нет.
Глубина вложенности технически допускается, но на практике больше двух уровней — признак того, что запрос пора переписать на пакетный с временными таблицами: читабельность падает катастрофически, а выигрыша в скорости нет.
В СКД вложенные запросы работают, но затрудняют использование конструктора и автоматическую оптимизацию схемы. Если отчёт строится на СКД и подзапрос становится громоздким, правильнее вынести подготовку данных в набор данных-запрос с временными таблицами или в объединение наборов.
Проверка результата: как убедиться, что запрос работает правильно
После написания запроса сверьте его результат с независимым источником: типовым отчётом, оборотно-сальдовой ведомостью или ручной выборкой по нескольким контрольным записям. Совпадение итоговых сумм — минимальный критерий корректности.
Проверьте граничные случаи: записи с пустыми значениями (NULL в соединениях), документы без движений, контрагенты без заказов. Именно на таких строках подзапросы с НЕ В и левыми соединениями чаще всего дают расхождения.
Как проверить результат
Совпадают с типовым отчётом или контрольной выборкой
Расхождение — проверять соединения и группировку
NULL обрабатываются явно, строки не теряются
Записи без связанных данных пропадают из результата
Укладывается в приемлемое для пользователей время
Резкий рост времени при увеличении объёма данных
Результат стабилен
Результат меняется — вероятна неоднозначность в подзапросе
Как выбрать действие
Частые вопросы о вложенных запросах в 1С
Можно ли использовать вложенный запрос в СКД?
Да, набор данных типа «Запрос» в СКД поддерживает вложенные запросы — они пишутся в тексте запроса обычным образом. Однако сложные подзапросы затрудняют работу конструктора и настройку полей. Если логика разрастается, лучше оформить подготовку данных через временные таблицы в том же тексте запроса или вынести часть логики в набор данных-объект.
Что быстрее: вложенный запрос или временная таблица?
Однозначного ответа нет — зависит от объёма данных и плана СУБД. На малых выборках разница незаметна. На больших объёмах временная таблица обычно выигрывает, потому что материализуется один раз и может быть проиндексирована. Правильный способ — замерить оба варианта на копии реальной базы.
Почему подзапрос в условии В работает медленно?
Если подзапрос возвращает большой список без подходящих индексов, СУБД может выполнять его повторно или строить неоптимальный план соединения. Помогает замена условия В на левое соединение с проверкой на NULL либо вынос списка во временную таблицу с индексом по полю соединения.
Поддерживаются ли коррелированные подзапросы в 1С?
Нет. Вложенный запрос не может обращаться к полям внешнего запроса — он всегда выполняется независимо. Связь между уровнями задаётся только через соединения и условия во внешнем запросе. Если нужна логика «для каждой строки вычислить по её значению», используйте соединение с группировкой.
Сколько уровней вложенности допустимо?
Технически платформа допускает несколько уровней, но практический предел — один, редко два. Более глубокая вложенность делает запрос нечитаемым, усложняет отладку и почти никогда не даёт выигрыша по сравнению с пакетным запросом с временными таблицами.
Когда подзапрос лучше заменить на соединение?
Когда подзапрос в списке полей вычисляет значение для каждой строки — например, «последняя цена товара». Такой скалярный подзапрос выполняется многократно. Замена на левое соединение с предварительно сгруппированной таблицей (или со срезом последних) выполняет вычисление один раз и обычно работает заметно быстрее.
Что важно запомнить
Вложенный запрос нужен, чтобы использовать результат одной выборки как условие или источник для другой в одном тексте запроса. Он уместен для компактной одноразовой логики, но на больших объёмах уступает временным таблицам и виртуальным таблицам регистров. Перед применением проверяйте результат на граничных случаях и замеряйте время выполнения на реальных данных.