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