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