В мире 1С:Предприятие запросы — это основа работы с данными. Но когда речь заходит о вложенных запросах, даже опытные разработчики иногда сомневаются: а не проще ли обойтись без них? На практике же вложенные запросы решают задачи, с которыми обычные «плоские» запросы справиться не в состоянии. Они позволяют строить сложную логику выборки, оптимизировать производительность и даже обходить ограничения платформы.

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

Если вы когда-нибудь сталкивались с задачами вроде «выбрать документы, где сумма по связанным табличным частям превышает X, но только для контрагентов из определенного региона» — эта статья для вас. Даже если вы новичок, после прочтения вы сможете уверенно применять вложенные запросы в своих разработках.

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

Вложенный запрос — это запрос внутри другого запроса, результат которого используется как часть основного. В 1С:Предприятие 8 они реализуются через конструкцию ВЫБРАТЬ ... ГДЕ [подзапрос] или ВЫБРАТЬ ... В (подзапрос). Главное отличие от обычных запросов — возможность динамически формировать условия выборки на основе промежуточных данных.

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

ВЫБРАТЬ

ЗаказыКлиентов.Ссылка КАК Заказ,

ЗаказыКлиентов.Контрагент КАК Клиент

ИЗ

Документ.ЗаказКлиента КАК ЗаказыКлиентов

ГДЕ

ЗаказыКлиентов.Контрагент В (

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

Документ.ЗаказКлиента.Контрагент

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Дата МЕЖДУ &НачалоМесяца И &КонецМесяца

ГРУППИРОВКА ПО

Заказы.Контрагент

ИМЕЮЩИЕ

СУММА(Заказы.СуммаДокумента) > 100000

)

Ключевые преимущества вложенных запросов:

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

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

📊 Как часто вы используете вложенные запросы в 1С?
Постоянно, без них никуда
Иногда, когда нет альтернативы
Редеко, предпочитаю временные таблицы
Никогда не использовал

5 сценариев, где вложенные запросы незаменимы

Есть задачи, где вложенные запросы не просто удобны, а являются единственным разумным решением. Рассмотрим типичные случаи.

1. Фильтрация по агрегированным данным

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

ВЫБРАТЬ

Номенклатура.Наименование,

Номенклатура.Артикул

ИЗ

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

ГДЕ

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

ВЫБРАТЬ

Документ.РеализацияТоваровУслуг.Номенклатура

ИЗ

Документ.РеализацияТоваровУслуг КАК Продажи

ГДЕ

Продажи.Дата МЕЖДУ &НачалоКвартала И &КонецКвартала

ГРУППИРОВКА ПО

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

ИМЕЮЩИЕ

СУММА(Продажи.Количество * Продажи.Цена) > 50000

)

2. Работа с иерархическими справочниками

Если нужно выбрать элементы справочника, у которых есть дочерние элементы с определенным признаком (например, «пометка на удаление»), вложенный запрос спасет от рекурсивных процедур:

ВЫБРАТЬ

Группы.Ссылка КАК Группа

ИЗ

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

ГДЕ

Группы.ЭтотГруппа = ИСТИНА

И СУЩЕСТВУЕТ (

ВЫБРАТЬ

РАЗРЕШЕННЫЕ 1

ИЗ

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

ГДЕ

Подчиненные.Родитель = Группы.Ссылка

И Подчиненные.ПометкаУдаления = ИСТИНА

)

3. Сравнение данных из разных периодов

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

ВЫБРАТЬ

ТекущийМесяц.Контрагент КАК Клиент,

ТекущийМесяц.Сумма КАК СуммаТекущая,

ПрошлыйМесяц.Сумма КАК СуммаПрошлая

ИЗ

(

ВЫБРАТЬ

Заказы.Контрагент,

СУММА(Заказы.СуммаДокумента) КАК Сумма

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Дата МЕЖДУ &НачалоТекущегоМесяца И &КонецТекущегоМесяца

ГРУППИРОВКА ПО

Заказы.Контрагент

) КАК ТекущийМесяц

ВНУТРЕННЕЕ СОЕДИНЕНИЕ

(

ВЫБРАТЬ

Заказы.Контрагент,

СУММА(Заказы.СуммаДокумента) КАК Сумма

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Дата МЕЖДУ &НачалоПрошлогоМесяца И &КонецПрошлогоМесяца

ГРУППИРОВКА ПО

Заказы.Контрагент

) КАК ПрошлыйМесяц

ПО

ТекущийМесяц.Контрагент = ПрошлыйМесяц.Контрагент

ГДЕ

ТекущийМесяц.Сумма < ПрошлыйМесяц.Сумма

4. Проверка существования связанных записей

Если нужно выбрать документы, для которых существуют связанные записи в другой таблице (например, оплаты по счетам), вложенный запрос с СУЩЕСТВУЕТ сработает эффективнее, чем ЛЕВОЕ СОЕДИНЕНИЕ:

ВЫБРАТЬ

Счета.Ссылка КАК Счет

ИЗ

Документ.СчетНаОплату КАК Счета

ГДЕ

СУЩЕСТВУЕТ (

ВЫБРАТЬ

РАЗРЕШЕННЫЕ 1

ИЗ

Документ.ПоступлениеНаРасчетныйСчет КАК Оплаты

ГДЕ

Оплаты.СчетНаОплату = Счета.Ссылка

)

5. Динамическое формирование списков значений

Когда список значений для фильтрации формируется динамически (например, на основе прав пользователя), вложенный запрос позволяет избежать жесткого кодирования:

ВЫБРАТЬ

Документы.Ссылка

ИЗ

Документ.ЗаказКлиента КАК Документы

ГДЕ

Документы.Контрагент В (

ВЫБРАТЬ

Элементы.Ссылка

ИЗ

Справочник.Контрагенты КАК Элементы

ГДЕ

Элементы.ПометкаУдаления = ЛОЖЬ

И Элементы.Регион В (&СписокРегионов)

)

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

💡

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

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

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

Когда вложенные запросы работают быстрее

  • 🚀 Фильтрация по небольшому подмножеству данных: если вложенный запрос возвращает мало строк (например, список из 10 контрагентов), его выполнение будет быстрее, чем соединение больших таблиц.
  • 🚀 Замена временных таблиц: вложенный запрос часто выполняется в памяти, тогда как временные таблицы требуют дискового ввода-вывода.
  • 🚀 Использование индексов: если поля в условиях вложенного запроса проиндексированы, СУБД оптимизирует выполнение.

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

ВЫБРАТЬ

Заказы.Ссылка

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Контрагент В (

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

Контрагенты.Ссылка

ИЗ

Справочник.Контрагенты КАК Контрагенты

ГДЕ

Контрагенты.Регион = &Регион

И Контрагенты.ВидыДеятельности.Содержит(&ВидДеятельности)

)

И Заказы.Дата > &ДатаНачала

Когда вложенные запросы тормозят систему

  • 🐢 Глубокая вложенность: 3–4 уровня вложенности и более могут значительно замедлить выполнение.
  • 🐢 Вложенные запросы в циклах: если вы выполняете запрос с вложенностью в цикле по большому количеству элементов, производительность упадет в разы.
  • 🐢 Отсутствие индексов: если поля в условиях не проиндексированы, СУБД будет сканировать всю таблицу.
  • 🐢 Возврат большого объема данных: если вложенный запрос возвращает тысячи строк, лучше использовать временные таблицы.

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

ВЫБРАТЬ

Товары.Наименование

ИЗ

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

ГДЕ

Товары.Ссылка В (

ВЫБРАТЬ

Заказы.Номенклатура

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Контрагент В (

ВЫБРАТЬ

Контрагенты.Ссылка

ИЗ

Справочник.Контрагенты КАК Контрагенты

ГДЕ

Контрагенты.Регион В (

ВЫБРАТЬ

Регионы.Ссылка

ИЗ

Справочник.Регионы КАК Регионы

ГДЕ

Регионы.Наименование ПОДОБНО "Моск%"

)

)

)

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

  1. Четырех уровней вложенности.
  2. Отсутствия явных индексов по полям Регион и Наименование.
  3. Использования ПОДОБНО без ограничения по индексированному полю.
💡

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

Как ускорить вложенные запросы: 5 практических советов

Проблема Решение Пример
Глубокая вложенность Разбивайте на несколько запросов или используйте временные таблицы Замените 4 уровня вложенности на 2 запроса с промежуточной таблицей
Отсутствие индексов Добавьте индексы по полям в условиях ГДЕ Создайте индекс по полю Регион в справочнике Контрагенты
Вложенные запросы в циклах Перенесите логику в один запрос с ГДЕ ... В Замените цикл по 1000 элементов на один запрос с фильтрацией
Большой объем данных во вложенном запросе Ограничьте выборку по дате или другим критериям Добавьте ГДЕ Дата > &ТекущаяДата - 365
Использование ПОДОБНО без индексов Замените на поиск по точному совпадению или используйте полнотекстовый поиск Вместо ПОДОБНО "Моск%" используйте = "Москва"

Если вы сомневаетесь в производительности, всегда тестируйте запрос на реальных данных с помощью ОбъяснитьЗапрос() или Консоли запросов в 1С. Это покажет, как именно СУБД выполняет запрос и где могут быть «бутылочные горлышки».

Как использовать ОбъяснитьЗапрос()?

Функция ОбъяснитьЗапрос() возвращает план выполнения запроса. Например:

План = ОбъяснитьЗапрос(ТекстЗапроса);

Сообщить(План);

Это поможет увидеть, используются ли индексы, сколько времени занимает каждый этап и где можно оптимизировать.

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

Даже опытные разработчики иногда допускают ошибки, которые ведут к медленной работе или некорректным результатам. Разберем самые распространенные из них.

1. Избыточная вложенность

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

Плохо:

ВЫБРАТЬ ...

ГДЕ Поле1 В (

ВЫБРАТЬ ...

ГДЕ Поле2 В (

ВЫБРАТЬ ...

ГДЕ Поле3 В (

ВЫБРАТЬ ...

)

)

)

Хорошо: Разбить на несколько запросов или использовать временные таблицы.

2. Неучет NULL-значений

Если вложенный запрос может вернуть NULL, сравнение с ним приведет к неожиданным результатам. Например, ГДЕ Поле В (ВЫБРАТЬ NULL) не вернет ни одной строки, даже если Поле тоже NULL.

Решение: Явно обрабатывайте NULL с помощью ЕСТЬNULL или ЗНАЧЕНИЕЗАПОЛНЕНО.

3. Использование вложенных запросов там, где достаточно соединения

Иногда вложенный запрос используется там, где проще и эффективнее сделать СОЕДИНЕНИЕ.

Плохо:

ВЫБРАТЬ

Заказы.Ссылка

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Контрагент В (

ВЫБРАТЬ

Контрагенты.Ссылка

ИЗ

Справочник.Контрагенты КАК Контрагенты

ГДЕ

Контрагенты.Регион = &Регион

)

Хорошо:

ВЫБРАТЬ

Заказы.Ссылка

ИЗ

Документ.ЗаказКлиента КАК Заказы

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

ПО Заказы.Контрагент = Контрагенты.Ссылка

ГДЕ

Контрагенты.Регион = &Регион

4. Вложенные запросы в условиях с агрегатными функциями

Если во вложенном запросе используется СУММА, МАКСИМУМ и т. п., а внешний запрос тоже агрегирует данные, это может привести к некорректным результатам или медленной работе.

Пример проблемы:

ВЫБРАТЬ

СУММА(Заказы.СуммаДокумента) КАК Итого

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.СуммаДокумента > (

ВЫБРАТЬ

СРЕДНЕЕ(ЗаказыВложенные.СуммаДокумента)

ИЗ

Документ.ЗаказКлиента КАК ЗаказыВложенные

)

Решение: Используйте подзапросы в секции FROM (в 1С это реализуется через временные таблицы).

5. Игнорирование кэширования

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

Решение: Вынесите повторяющийся вложенный запрос в отдельную переменную или используйте временную таблицу.

Пример оптимизации:

// Плохо: запрос выполняется для каждого элемента цикла

Для Каждого Товар Из СписокТоваров Цикл

Результат = ВыполнитьЗапрос(

"ВЫБРАТЬ ...

ГДЕ Номенклатура = &Номенклатура",

Новый Структура("Номенклатура", Товар)

);

КонецЦикла;

// Хорошо: результат кэшируется

СписокКонтрагентов = ВыполнитьЗапрос(

"ВЫБРАТЬ РАЗЛИЧНЫЕ Контрагенты.Ссылка ИЗ Справочник.Контрагенты КАК Контрагенты"

).Выгрузить();

Для Каждого Товар Из СписокТоваров Цикл

Результат = ВыполнитьЗапрос(

"ВЫБРАТЬ ...

ГДЕ Номенклатура = &Номенклатура

И Контрагент В (&СписокКонтрагентов)",

Новый Структура("Номенклатура, СписокКонтрагентов", Товар, СписокКонтрагентов)

);

КонецЦикла;

Использована минимально необходимая вложенность|Все поля в условиях проиндексированы|Нет избыточных соединений вместо вложенных запросов|Учтена возможность NULL-значений|Запрос протестирован на реальных данных-->

Вложенные запросы vs временные таблицы: что выбрать

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

Когда выбрать вложенные запросы

  • Малый или средний объем данных (до 10 000 строк во вложенном запросе).
  • Простая логика (1–2 уровня вложенности).
  • Нужна компактность кода (например, в отчетах или небольших обработках).
  • Динамические условия (когда список значений формируется на лету).

Когда выбрать временные таблицы

  • Большой объем данных (десятки тысяч строк и более).
  • Сложные многоуровневые условия (3+ уровня вложенности).
  • Необходимость повторного использования (если результат нужен в нескольких местах).
  • Работа в циклах (чтобы избежать повторных обращений к базе).

Пример, где временная таблица эффективнее:

// 1. Создаем временную таблицу с контрагентами из нужного региона

ВременнаяТаблица = Новый ТаблицаЗначений;

ВременнаяТаблица.Колонки.Добавить("Контрагент", Новый ОписаниеТипов("СправочникСсылка.Контрагенты"));

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

Контрагенты.Ссылка КАК Контрагент

ИЗ

Справочник.Контрагенты КАК Контрагенты

ГДЕ

Контрагенты.Регион = &Регион";

Запрос.УстановитьПараметр("Регион", Регион);

Результат = Запрос.Выполнить();

Выборка = Результат.Выбрать();

Пока Выборка.Следующий() Цикл

ВременнаяТаблица.Добавить();

ВременнаяТаблица.Последний().Контрагент = Выборка.Контрагент;

КонецЦикла;

// 2. Используем временную таблицу в основном запросе

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

Заказы.Ссылка

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Контрагент В (&Контрагенты)";

Запрос.УстановитьПараметр("Контрагенты", ВременнаяТаблица);

Результат = Запрос.Выполнить();

А здесь вложенный запрос уместнее:

// Простой запрос с одним уровнем вложенности

Запрос = Новый Запрос;

Запрос.Текст =

"ВЫБРАТЬ

Заказы.Ссылка

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Контрагент В (

ВЫБРАТЬ

Контрагенты.Ссылка

ИЗ

Справочник.Контрагенты КАК Контрагенты

ГДЕ

Контрагенты.Регион = &Регион

)";

Запрос.УстановитьПараметр("Регион", Регион);

Результат = Запрос.Выполнить();

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

💡

Если вы сомневаетесь, какой подход выбрать, протестируйте оба варианта на реальных данных с помощью ОбъяснитьЗапрос() и замерьте время выполнения.

Практические примеры: от простых к сложным

Теория — это хорошо, но без практики она мало что значит. Разберем несколько реальных примеров, от базовых до продвинутых.

Пример 1: Простая фильтрация по справочнику

Задача: Выбрать все заказы клиентов из Москвы.

Решение:

ВЫБРАТЬ

Заказы.Ссылка КАК Заказ,

Заказы.Дата КАК ДатаЗаказа

ИЗ

Документ.ЗаказКлиента КАК Заказы

ГДЕ

Заказы.Контрагент В (

ВЫБРАТЬ

Контрагенты.Ссылка

ИЗ

Справочник.Контрагенты КАК Контрагенты

ГДЕ

Контрагенты.Город = "Москва"

)

Пример 2: Агрегация с условием

Задача: Найти номенклатуру, по которой было продано более 100 единиц за последний месяц.

Решение:

ВЫБРАТЬ

Номенклатура.Наименование,

Номенклатура.Артикул

ИЗ

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

ГДЕ

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

ВЫБРАТЬ

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

ИЗ

Документ.РеализацияТоваровУслуг КАК Продажи

ГДЕ

Продажи.Дата МЕЖДУ &НачалоМесяца И &КонецМесяца

ГРУППИРОВКА ПО

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

ИМЕЮЩИЕ

СУММА(Продажи.Количество) > 100

)

Пример 3: Сравнение с подзапросом

Задача: Выбрать документы,