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

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

Подзапрос в ГДЕ в 1С: как фильтровать данные через вложенные запросы

Как использовать подзапрос в условии ГДЕ языка запросов 1С: операторы В и В ИЕРАРХИИ, примеры кода, типичные ошибки и способы оптимизации.

Подзапрос в условии ГДЕ — один из основных приёмов фильтрации данных в языке запросов 1С. Он позволяет отобрать записи, значения которых входят (или не входят) в результат другого запроса, без предварительной выгрузки данных во временную таблицу. Типичная задача: найти все документы, контрагенты которых есть в списке должников, или товары, по которым не было продаж за период.

В языке запросов 1С подзапрос в ГДЕ чаще всего применяется вместе с операторами В, НЕ В и В ИЕРАРХИИ. Подзапрос заключается в круглые скобки и может содержать полноценную выборку с собственными соединениями, группировками и условиями. Это делает конструкцию гибкой, но требует понимания того, как платформа выполняет такой запрос на стороне СУБД.

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

Базовый синтаксис подзапроса в условии ГДЕ

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

ВЫБРАТЬ

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

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

ИЗ

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

ГДЕ

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

(ВЫБРАТЬ

Должники.Контрагент

ИЗ

РегистрСведений.Должники КАК Должники)

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

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

Оператор В ИЕРАРХИИ с подзапросом

Для иерархических справочников (номенклатура, статьи затрат, подразделения) используется оператор В ИЕРАРХИИ. Он отбирает не только сами элементы из результата подзапроса, но и все их дочерние элементы по иерархии.

ГДЕ

Товары.Номенклатура В ИЕРАРХИИ

(ВЫБРАТЬ

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

ИЗ

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

ГДЕ

Номенклатура.Родитель = &ГруппаЭлектроника)

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

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

Сравнение по нескольким полям

Язык запросов 1С поддерживает сравнение кортежей: когда в подзапросе выбирается несколько полей, слева от оператора В перечисляется такой же набор полей в том же порядке. Порядок и типы полей должны совпадать, иначе запрос не выполнится или вернёт неверный результат.

ГДЕ

(Товары.Номенклатура, Товары.Склад) В

(ВЫБРАТЬ

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

Остатки.Склад

ИЗ

РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки

ГДЕ

Остатки.КоличествоОстаток > 0)

Такая конструкция заменяет цепочку условий И и упрощает логику, когда пара значений осмысленна только вместе — например, товар на конкретном складе. Однако не все СУБД одинаково оптимизируют сравнение кортежей, поэтому на крупных базах стоит сравнить производительность с вариантом через соединение.

Подзапрос против временной таблицы: что выбрать

Любой подзапрос в ГДЕ можно переписать через временную таблицу: сначала выбрать значения в таблицу с ПОМЕСТИТЬ, затем соединиться с ней или использовать её в условии. Результат логически тот же, но отличаются читаемость, отладка и производительность.

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

Подзапрос в ГДЕ

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

Временная таблица + соединение

Промежуточные данные можно проверить отдельным запросом, менеджер временных таблиц переиспользуется в пакете. Часто быстрее на больших объёмах, но требует больше кода.

Временная таблица + условие В

Гибрид: таблица готовится один раз, условие остаётся коротким. Удобно, когда один и тот же список значений используется в нескольких запросах пакета.

Практическое правило: если подзапрос простой (одна таблица, одно лёгкое условие) — оставляйте его в ГДЕ. Если внутри соединения, группировки или виртуальные таблицы с параметрами — выносите во временную таблицу. Это также позволяет проиндексировать временную таблицу через ИНДЕКСИРОВАТЬ ПО, что заметно ускоряет последующее соединение.

ВЫБРАТЬ

Должники.Контрагент

ПОМЕСТИТЬ ВТ_Должники

ИЗ

РегистрСведений.Должники КАК Должники

ИНДЕКСИРОВАТЬ ПО

Должники.Контрагент

;

ВЫБРАТЬ

Реализация.Ссылка

ИЗ

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

ГДЕ

Реализация.Контрагент В

(ВЫБРАТЬ Т.Контрагент ИЗ ВТ_Должники КАК Т)

Типичные ошибки при работе с подзапросами

Большинство проблем с подзапросами в ГДЕ сводится к нескольким повторяющимся сценариям. Знание их заранее экономит время отладки.

Типичные ошибки
01
Несовпадение типов полей: сравнивается ссылка на справочник со строкой или числом — результат пустой либо ошибка исполнения.
02
Подзапрос возвращает несколько полей, а слева одно поле — платформа выдаёт ошибку о несоответствии числа полей.
03
Использование НЕ В при возможных NULL в результате подзапроса: условие может не сработать так, как ожидается, — отфильтруйте NULL внутри подзапроса.
04
Тяжёлый подзапрос с виртуальной таблицей без отборов: читается вся таблица, запрос выполняется минутами.
05
Дублирование одного и того же подзапроса в нескольких условиях вместо одной временной таблицы.

Отдельно стоит сказать про NULL. Если подзапрос может вернуть NULL, условие НЕ В ведёт себя неочевидно: строки, для которых сравнение даёт неопределённый результат, исключаются из выборки. Безопасный приём — добавить в подзапрос условие «Поле ЕСТЬ НЕ NULL» или использовать ЕСТЬNULL при сравнении.

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

Проверка результата и производительности

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

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

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

Результат подзапроса отдельно
Норма

Возвращает ожидаемый набор значений в консоли запросов

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

Пустой результат или лишние строки — ошибка в условиях подзапроса

Типы сравниваемых полей
Норма

Совпадают по типу и составу (для составных полей)

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

Ссылка сравнивается со строкой, запрос пустой

Время выполнения на реальной базе
Норма

Запрос отрабатывает за приемлемое время

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

Долгое выполнение — смотреть план запроса, выносить во временную таблицу

NULL в подзапросе
Норма

NULL отфильтрованы условием ЕСТЬ НЕ NULL

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

НЕ В работает непредсказуемо, строки теряются

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

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

Часть задач, которые выглядят как «подзапрос в ГДЕ», решается проще. Если отбираете по списку значений, известному заранее, — передавайте список параметром: ГДЕ Поле В (&СписокЗначений). Платформа сама преобразует массив или список в табличный параметр.

Если нужно исключить записи, имеющие связанные строки (антиотбор), надёжнее работает ЛЕВОЕ СОЕДИНЕНИЕ с условием «связанная таблица ЕСТЬ NULL». Этот приём часто быстрее, чем НЕ В, и предсказуемее при NULL.

ВЫБРАТЬ

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

ИЗ

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

ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты КАК Продажи

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

ГДЕ

Продажи.Номенклатура ЕСТЬ NULL

Маршрут решения

Как выбрать действие

Список значений известен заранее и формируется в кодеПередайте массив параметром: ГДЕ Поле В (&Список)
Подзапрос простой, одна таблица, лёгкое условиеОставьте подзапрос в ГДЕ — это читаемо и достаточно быстро
Подзапрос содержит соединения, группировку или виртуальную таблицуВынесите во временную таблицу с ИНДЕКСИРОВАТЬ ПО
Нужен антиотбор (записи без связанных строк)Используйте ЛЕВОЕ СОЕДИНЕНИЕ + ЕСТЬ NULL вместо НЕ В
Один и тот же набор значений нужен в нескольких запросахПоместите его во временную таблицу один раз и переиспользуйте

Частые вопросы

Можно ли использовать подзапрос в ГДЕ с параметрами внутри?

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

Почему запрос с НЕ В возвращает пустой результат, хотя данные есть?

Наиболее вероятная причина — NULL среди значений подзапроса. Сравнение с NULL даёт неопределённый результат, и условие НЕ В перестаёт срабатывать. Добавьте в подзапрос условие «Поле ЕСТЬ НЕ NULL» и проверьте результат снова.

Что быстрее: подзапрос в ГДЕ или соединение с временной таблицей?

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

Можно ли в подзапросе обращаться к полям внешнего запроса?

Нет, в языке запросов 1С подзапрос в условии ГДЕ не коррелирует с внешней выборкой: он выполняется как самостоятельная выборка. Если нужна корреляция, переписывайте логику через соединение таблиц.

Как отобрать элементы нескольких групп справочника с учётом иерархии?

Используйте В ИЕРАРХИИ с подзапросом, возвращающим нужные группы: ГДЕ Номенклатура В ИЕРАРХИИ (ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ Ссылка В (&СписокГрупп)). Проверка иерархии выполнится для каждой группы из списка.

Работает ли подзапрос в ГДЕ в динамических списках и СКД?

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

Итог

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

Подзапрос в ГДЕ пишется через В, НЕ В или В ИЕРАРХИИ с выборкой в скобках. Следите за типами полей и NULL, тяжёлые подзапросы выносите во временные таблицы с индексом, а антиотборы надёжнее делать через ЛЕВОЕ СОЕДИНЕНИЕ с ЕСТЬ NULL.