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

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

Как создать запрос в запросе 1С: вложенные запросы и временные таблицы

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

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

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

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

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

Вложенный запрос — это полноценная конструкция ВЫБРАТЬ ... ИЗ ..., помещённая в круглые скобки внутри другого запроса. Снаружи он выглядит как обычная таблица: ему присваивается псевдоним через КАК, и его поля доступны в основной выборке. Платформа 1С при выполнении преобразует такую конструкцию в SQL-подзапрос.

Типовые ситуации, где без вложенного запроса обойтись сложно:

  • нужно сгруппировать данные, а потом отфильтровать или соединить уже сгруппированный результат (например, суммы продаж по контрагентам, превышающие порог);
  • требуется сравнить строку с агрегатом — выбрать документы, где сумма больше средней по всем документам;
  • нужно получить «последние» данные — максимальную дату, последний документ, актуальную цену — и по ним выбрать детали;
  • условие отбора само является списком значений, полученным запросом (конструкция В (...)).

Простые фильтры по полям таблицы вложенности не требуют: если условие выражается напрямую через ГДЕ по полям источника, вложенный запрос только усложнит чтение и может замедлить выполнение.

Синтаксис вложенного запроса в секции ИЗ

Самый частый вариант — вложенный запрос как источник данных. Он записывается в секции ИЗ в скобках, после закрывающей скобки обязательно указывается псевдоним через КАК. Без псевдонима конструктор запроса выдаст ошибку.

Пример: выбрать контрагентов, у которых сумма продаж за период превышает заданный порог, и показать саму сумму.

ВЫБРАТЬ

ПродажиПоКонтрагентам.Контрагент КАК Контрагент,

ПродажиПоКонтрагентам.СуммаПродаж КАК СуммаПродаж

ИЗ

(ВЫБРАТЬ

ПродажиОбороты.Контрагент КАК Контрагент,

СУММА(ПродажиОбороты.СуммаОборот) КАК СуммаПродаж

ИЗ

РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ПродажиОбороты

СГРУППИРОВАТЬ ПО

ПродажиОбороты.Контрагент) КАК ПродажиПоКонтрагентам

ГДЕ

ПродажиПоКонтрагентам.СуммаПродаж > &Порог

Обратите внимание на порядок: сначала внутренний запрос группирует обороты по контрагентам, затем внешний фильтрует уже готовый результат. Если попытаться наложить условие на сумму напрямую в секции ГДЕ без вложенности, платформа потребует использовать ИМЕЮЩИЕ — это рабочая альтернатива для простых случаев, но вложенный запрос даёт больше гибкости, когда к агрегату нужно ещё и присоединить другие таблицы.

К вложенному запросу в секции ИЗ применимы все виды соединений: его можно соединять с физическими таблицами и другими вложенными запросами через ЛЕВОЕ СОЕДИНЕНИЕ, ВНУТРЕННЕЕ СОЕДИНЕНИЕ и так далее.

Вложенный запрос в условии ГДЕ и оператор В

Второй сценарий — вложенный запрос как источник списка значений для фильтрации. Он помещается в секцию ГДЕ внутри оператора В (...) или НЕ В (...).

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

ВЫБРАТЬ

Номенклатура.Ссылка КАК Номенклатура

ИЗ

Справочник.Номенклатура КАК Номенклатура

ГДЕ

НЕ Номенклатура.Ссылка В

(ВЫБРАТЬ РАЗЛИЧНЫЕ

ПродажиОбороты.Номенклатура

ИЗ

РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ПродажиОбороты)

Внутри оператора В вложенный запрос должен возвращать одно поле — то, которое сравнивается с проверяемым значением. Если вернуть несколько полей, конструктор сообщит об ошибке несоответствия типов. Полезно добавлять РАЗЛИЧНЫЕ, чтобы список не содержал дублей: на результат это не влияет, но упрощает работу СУБД.

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

Временные таблицы через ПОМЕСТИТЬ как альтернатива

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

ВЫБРАТЬ

ПродажиОбороты.Контрагент КАК Контрагент,

СУММА(ПродажиОбороты.СуммаОборот) КАК СуммаПродаж

ПОМЕСТИТЬ ВТ_Продажи

ИЗ

РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, , ) КАК ПродажиОбороты

СГРУППИРОВАТЬ ПО

ПродажиОбороты.Контрагент

;

ВЫБРАТЬ

ВТ_Продажи.Контрагент КАК Контрагент,

ВТ_Продажи.СуммаПродаж КАК СуммаПродаж,

Контрагенты.ОсновнойМенеджер КАК Менеджер

ИЗ

ВТ_Продажи КАК ВТ_Продажи

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты

ПО ВТ_Продажи.Контрагент = Контрагенты.Ссылка

ГДЕ

ВТ_Продажи.СуммаПродаж > &Порог

Отдельные запросы пакета разделяются точкой с запятой. Временная таблица живёт в пределах одного объекта Запрос и одного менеджера временных таблиц. После использования её рекомендуется удалять инструкцией УНИЧТОЖИТЬ ВТ_Продажи, особенно если пакет длинный и таблица объёмная — это освобождает память сервера СУБД.

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

Сравнение вариантов

Вложенный запрос в скобках

Подходит для простой двухступенчатой логики и фильтров через В (...). Ограничение: плохо читается при трёх и более уровнях вложенности, сложнее отлаживать по частям.

Временная таблица (ПОМЕСТИТЬ)

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

ИМЕЮЩИЕ без вложенности

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

Пошаговый порядок создания вложенного запроса в конфигураторе

Надёжнее всего собирать вложенный запрос через консоль запросов или конструктор, а не писать текст «вслепую» в модуле. Порядок действий:

  1. Сформулируйте, какие данные должен вернуть внутренний запрос, и напишите его как самостоятельный запрос. Выполните и проверьте результат.
  2. Напишите внешний запрос, временно подставив вместо вложенного источника реальную таблицу или временную таблицу с теми же полями.
  3. Замените источник внешнего запроса на вложенный: в конструкторе запросов на закладке «Таблицы и поля» используйте кнопку добавления и выберите «Вложенный запрос», либо вручную оберните текст в скобки и добавьте псевдоним через КАК.
  4. Проверьте, что все поля вложенного запроса, используемые снаружи, имеют явные псевдонимы — платформа обращается к ним по именам.
  5. Выполните запрос на реальных данных и сверьте количество строк и итоги с контрольным отчётом.
  6. Перенесите текст в модуль, подставьте параметры через Запрос.УстановитьПараметр() и проверьте выполнение из кода.
Порядок действий
  1. 1
    Напишите и отладьте внутренний запрос отдельно
  2. 2
    Соберите внешний запрос на временных данных
  3. 3
    Оберните внутренний запрос в скобки и задайте псевдоним через КАК
  4. 4
    Проверьте псевдонимы полей и выполните запрос целиком
  5. 5
    Перенесите текст в модуль и задайте параметры
  6. 6
    Сверьте результат с контрольным отчётом

Проверка результата и типичные ошибки

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

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

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

Количество строк результата
Норма

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

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

Строк больше из-за дублей от соединения или меньше из-за лишнего отбора

Поля вложенного запроса
Норма

У всех полей есть псевдонимы, доступные снаружи

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

Конструктор ругается на неизвестное поле внешнего запроса

Суммы по агрегатам
Норма

Итоги по группам сходятся с регистром за тот же период

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

Суммы завышены из-за соединения до группировки вместо после

Параметры периода
Норма

Границы периода переданы во все вложенные уровни, где они нужны

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

Внутренний запрос берёт данные за всё время, фильтр только снаружи

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

  • Отсутствует псевдоним вложенного запроса. После закрывающей скобки нет КАК Имя — запрос не проходит проверку синтаксиса.
  • Дубли строк после соединения. Вложенный запрос возвращает несколько строк на ключ соединения, и итоговая выборка «раздувается». Лечится группировкой внутри вложенного запроса до соединения.
  • Фильтр по периоду применён только снаружи. Внутренний запрос сканирует весь регистр, что резко замедляет выполнение на больших базах. Параметры периода нужно передавать на самый глубокий уровень.
  • NULL в списке для НЕ В. Как отмечено выше, это может обнулить весь результат.
  • Использование вложенности там, где хватит ИМЕЮЩИЕ. Лишний уровень усложняет запрос и план его выполнения без выигрыша.
Типичные ошибки
01
Нет псевдонима после закрывающей скобки — синтаксическая ошибка при проверке запроса
02
Соединение с неагрегированным вложенным запросом — дубли строк и завышенные суммы
03
Параметры периода переданы только во внешний запрос — полное сканирование регистра и медленная работа
04
НЕ В (...) без исключения NULL — неожиданно пустой результат
05
Вложенность ради вложенности там, где достаточно ИМЕЮЩИЕ — запрос сложнее читать и отлаживать

Ограничения платформы и производительность

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

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

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

Итог

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

Запрос в запросе 1С делается либо вложенным запросом в скобках с обязательным псевдонимом, либо через временную таблицу с ПОМЕСТИТЬ. Фильтры и параметры периода передавайте на самый глубокий уровень, группируйте до соединений, а сложную логику отлаживайте по частям через временные таблицы.

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

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

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

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

Да, параметры вида &ИмяПараметра работают на любом уровне вложенности. Параметр задаётся один раз через Запрос.УстановитьПараметр() и доступен во всех частях запроса, включая самые глубокие. Именно так следует передавать границы периода во внутренние выборки.

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

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

Почему запрос с НЕ В (вложенный запрос) вернул пустой результат?

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

Можно ли упорядочить данные внутри вложенного запроса?

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

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

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