Работа с большими объемами данных в платформе 1С:Предприятие часто требует извлечения информации из нескольких связанных таблиц одновременно. Простого перебора циклом в этом случае недостаточно, так как это приводит к критическому падению производительности. Решением этой задачи становится вложенный запрос, позволяющий формировать сложные выборки непосредственно на уровне СУБД.

Использование подзапросов открывает разработчику доступ к мощным инструментам агрегации данных, фильтрации по сложным условиям и соединениям, которые невозможно реализовать в один проход обычным ВЫБРАТЬ. Понимание логики работы внутренних запросов является ключевым навыком для создания эффективных отчетов и обработок.

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

Базовый синтаксис и область видимости

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

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

Область видимости полей строго ограничена. Поля, определенные во внутреннем запросе, доступны во внешнем, но обратное утверждение неверно. Вы не можете обратиться к полю внешней таблицы изнутри подзапроса, если это не коррелированный подзапрос (о которых речь пойдет ниже).

⚠️ Внимание: При использовании вложенных запросов убедитесь, что имена полей во внутреннем запросе уникальны. Если два поля имеют одинаковое имя, внешнему запросу будет доступно только последнее из них в списке выбора.

Для объявления вложенного запроса используется конструкция ИЗ (ВЫБРАТЬ ...). Внутри скобок может находиться любой валидный запрос 1С, включая объединения и соединения.

ВЫБРАТЬ

ВложенныйЗапрос.Номенклатура,

ВложенныйЗапрос.СуммаОстатка

ИЗ

(ВЫБРАТЬ

РегистрНакопления.ОстаткиНоменклатуры.Номенклатура,

СУММА(РегистрНакопления.ОстаткиНоменклатуры.Количество) КАК СуммаОстатка

ИЗ

РегистрНакопления.ОстаткиНоменклатуры

ГДЕ

РегистрНакопления.ОстаткиНоменклатуры.Период МЕЖДУ &НачПериода И &КонПериода

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

РегистрНакопления.ОстаткиНоменклатуры.Номенклатура) КАК ВложенныйЗапрос

ГДЕ

ВложенныйЗапрос.СуммаОстатка > 0

Фильтрация данных с помощью оператора В

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

Этот подход значительно эффективнее, чем получение списка ID в код и последующая подстановка их в текст запроса через запятую. СУБД сама оптимизирует выполнение такого условия, используя индексы там, где это возможно.

Рассмотрим пример, где необходимо выбрать все документы реализации, контрагенты которых имеют задолженность. Мы сначала выбираем список должников, а затем фильтруем документы по этому списку.

  • 📌 Подзапрос в условии В должен возвращать только одно поле (колонку), тип которого совместим с полем внешнего запроса.
  • 🚀 Использование В вместо множественных условий ИЛИ упрощает чтение кода и ускоряет его выполнение на больших выборках.
  • 🔍 Если подзапрос возвращает пустой результат, условие В всегда ложно, и внешний запрос вернет ноль строк.

Синтаксически это выглядит следующим образом:

ВЫБРАТЬ

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

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

ИЗ

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

ГДЕ

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

(ВЫБРАТЬ

РегистрБухгалтерии.Хозрасчетный.СчетДт

ИЗ

РегистрБухгалтерии.Хозрасчетный

ГДЕ

РегистрБухгалтерии.Хозрасчетный.СчетДт = &СчетДебиторскойЗадолженности)

📊 Какой способ фильтрации вы используете чаще?
Через оператор В (подзапрос)
Через Временную таблицу
Через цикл в коде
Через соединение (JOIN)

Агрегация данных и вычисляемые поля

Часто возникает ситуация, когда необходимо отфильтровать записи по результату агрегатной функции, например, по сумме или количеству. Прямое использование СУММА() в условии ГДЕ невозможно, так как агрегация происходит после фильтрации.

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

Это позволяет реализовать логику "найти товары, общая продажа которых за год превысила 1 миллион". Без вложенного запроса пришлось бы выгружать все продажи в память и суммировать их вручную, что недопустимо медленно.

⚠️ Внимание: Помните, что агрегатные функции (СУММА, МИНИМУМ, МАКСИМУМ) работают только с числовыми полями или полями типа Дата. Попытка применить их к строке вызовет ошибку выполнения.

Важно правильно группировать данные. Все поля, не участвующие в агрегатных функциях, должны быть перечислены в секции СГРУППИРОВАТЬ ПО внутреннего запроса.

💡

Используйте функцию ЕСТЬNULL() внутри вложенного запроса, если есть риск деления на ноль или работы с пустыми значениями перед агрегацией. Это предотвратит ошибки выполнения.

Коррелированные подзапросы

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

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

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

ВЫБРАТЬ

ВнешниеДанные.Ссылка,

ВнешниеДанные.Дата

ИЗ

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

ГДЕ

НЕ СУЩЕСТВУЕТ

(ВЫБРАТЬ

ВнутренниеДанные.Ссылка

ИЗ

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

ГДЕ

ВнутренниеДанные.Контрагент = ВнешниеДанные.Контрагент

И ВнутренниеДанные.Дата > ВнешниеДанные.Дата)

  • ⚡ Коррелированные подзапросы могут сильно замедлить работу, если внешний запрос возвращает много строк.
  • 🛠 В современных версиях 1С и СУБД такие запросы часто оптимизируются автоматически, но полагаться на это не стоит.
  • ✅ Старайтесь заменять коррелированные подзапросы на соединения (ЛЕВОЕ СОЕДИНЕНИЕ) или временные таблицы, где это возможно.
Почему коррелированные запросы медленные?

Потому что внутренний запрос выполняется повторно для КАЖДОЙ строки внешнего результата. Если снаружи 10 000 строк, то внутренний запрос сработает 10 000 раз.

Оптимизация и использование временных таблиц

Хотя вложенные запросы удобны, их глубокое nesting (вложение одного в другой) может затруднить работу оптимизатора запросов СУБД. Если логика становится слишком сложной, лучшим решением является использование временных таблиц.

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

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

Критерий Вложенный запрос Временная таблица
Читаемость кода Низкая при большой вложенности Высокая, логика разбита на шаги
Производительность Зависит от оптимизатора СУБД Стабильная, можно управлять индексами
Повторное использование Только в рамках одного запроса Можно использовать в нескольких запросах
Отладка Сложно посмотреть промежуточный результат Легко вывести содержимое таблицы
⚠️ Внимание: При работе в облачных версиях 1С или на слабых серверах создание огромных временных таблиц может исчерпать доступную оперативную память. Всегда оценивайте ожидаемый объем данных.

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

💡

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

Частые ошибки и лучшие практики

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

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

Еще одна проблема — использование РАЗЛИЧНЫЕ (DISTINCT) без необходимости. Этот оператор заставляет СУБД выполнять дополнительную сортировку и удаление дублей, что ресурсоемко. Часто дубли возникают из-за неправильных соединений, и лечить нужно причину, а не следствие.

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

☑️ Чек-лист проверки запроса

Выполнено: 0 / 5

FAQ: Вопросы и ответы

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

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

В чем разница между ПОДОБНО и вложенным запросом для поиска?

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

Почему мой вложенный запрос работает медленно?

Возможные причины: отсутствие индексов по полям соединения, использование функций в условиях ГДЕ (например, ГОД(Дата)), коррелированный подзапрос на большом объеме данных или отсутствие фильтрации по периоду. Используйте анализ плана выполнения запроса для точной диагностики.

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

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

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

Самый эффективный способ — создать временную таблицу в коде, заполнить её значениями из кода и передать эту таблицу как параметр в запрос. В тексте запроса вы будете обращаться к ней как к обычной таблице: ИЗ &МояТаблица КАК Парам.