Разработка и Конфигурация · 11 мин
Подзапрос в ГДЕ в 1С: как фильтровать данные через вложенные запросы
Как использовать подзапрос в условии ГДЕ языка запросов 1С: операторы В и В ИЕРАРХИИ, примеры кода, типичные ошибки и способы оптимизации.
Подзапрос в условии ГДЕ — один из основных приёмов фильтрации данных в языке запросов 1С. Он позволяет отобрать записи, значения которых входят (или не входят) в результат другого запроса, без предварительной выгрузки данных во временную таблицу. Типичная задача: найти все документы, контрагенты которых есть в списке должников, или товары, по которым не было продаж за период.
В языке запросов 1С подзапрос в ГДЕ чаще всего применяется вместе с операторами В, НЕ В и В ИЕРАРХИИ. Подзапрос заключается в круглые скобки и может содержать полноценную выборку с собственными соединениями, группировками и условиями. Это делает конструкцию гибкой, но требует понимания того, как платформа выполняет такой запрос на стороне СУБД.
Материал разбирает синтаксис, рабочие примеры, ограничения и альтернативные подходы — временные таблицы и соединения, которые в ряде случаев работают быстрее и читаются проще.
Базовый синтаксис подзапроса в условии ГДЕ
Подзапрос размещается в круглых скобках после ключевого слова В. Внутри скобок — обычный запрос, который должен возвращать одно поле (или набор полей, если сравнивается кортеж). Платформа сравнивает значение поля основной выборки с каждой строкой результата подзапроса.
ВЫБРАТЬ
РеализацияТоваровУслуг.Ссылка,
РеализацияТоваровУслуг.Контрагент
ИЗ
Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
ГДЕ
РеализацияТоваровУслуг.Контрагент В
(ВЫБРАТЬ
Должники.Контрагент
ИЗ
РегистрСведений.Должники КАК Должники)
Обратное условие строится через НЕ В — отбираются записи, значения которых отсутствуют в результате подзапроса. Именно так решается задача «товары без движений за период» или «контрагенты без документов».
Важно помнить: подзапрос в ГДЕ выполняется логически для всей выборки, и если внутри него тяжёлые соединения без индексированных условий, время выполнения растёт резко. На больших таблицах всегда проверяйте план запроса в консоли запросов или через технологический журнал.
Оператор В ИЕРАРХИИ с подзапросом
Для иерархических справочников (номенклатура, статьи затрат, подразделения) используется оператор В ИЕРАРХИИ. Он отбирает не только сами элементы из результата подзапроса, но и все их дочерние элементы по иерархии.
ГДЕ
Товары.Номенклатура В ИЕРАРХИИ
(ВЫБРАТЬ
Номенклатура.Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Родитель = &ГруппаЭлектроника)
Если подзапрос возвращает несколько групп, проверка иерархии выполняется для каждой из них. Это удобно, когда нужно отобрать документы по нескольким разделам номенклатуры одновременно — без перечисления всех дочерних элементов вручную.
Учтите, что проверка иерархии — относительно дорогая операция: для каждой строки СУБД должна определить принадлежность к ветке дерева. На больших табличных частях иногда выгоднее заранее развернуть иерархию во временную таблицу и соединиться с ней напрямую.
Сравнение по нескольким полям
Язык запросов 1С поддерживает сравнение кортежей: когда в подзапросе выбирается несколько полей, слева от оператора В перечисляется такой же набор полей в том же порядке. Порядок и типы полей должны совпадать, иначе запрос не выполнится или вернёт неверный результат.
ГДЕ
(Товары.Номенклатура, Товары.Склад) В
(ВЫБРАТЬ
Остатки.Номенклатура,
Остатки.Склад
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
ГДЕ
Остатки.КоличествоОстаток > 0)
Такая конструкция заменяет цепочку условий И и упрощает логику, когда пара значений осмысленна только вместе — например, товар на конкретном складе. Однако не все СУБД одинаково оптимизируют сравнение кортежей, поэтому на крупных базах стоит сравнить производительность с вариантом через соединение.
Подзапрос против временной таблицы: что выбрать
Любой подзапрос в ГДЕ можно переписать через временную таблицу: сначала выбрать значения в таблицу с ПОМЕСТИТЬ, затем соединиться с ней или использовать её в условии. Результат логически тот же, но отличаются читаемость, отладка и производительность.
Сравнение вариантов
Компактно, весь запрос в одном тексте. Подходит для простых выборок. Сложнее отлаживать: промежуточный результат подзапроса не виден отдельно.
Промежуточные данные можно проверить отдельным запросом, менеджер временных таблиц переиспользуется в пакете. Часто быстрее на больших объёмах, но требует больше кода.
Гибрид: таблица готовится один раз, условие остаётся коротким. Удобно, когда один и тот же список значений используется в нескольких запросах пакета.
Практическое правило: если подзапрос простой (одна таблица, одно лёгкое условие) — оставляйте его в ГДЕ. Если внутри соединения, группировки или виртуальные таблицы с параметрами — выносите во временную таблицу. Это также позволяет проиндексировать временную таблицу через ИНДЕКСИРОВАТЬ ПО, что заметно ускоряет последующее соединение.
ВЫБРАТЬ
Должники.Контрагент
ПОМЕСТИТЬ ВТ_Должники
ИЗ
РегистрСведений.Должники КАК Должники
ИНДЕКСИРОВАТЬ ПО
Должники.Контрагент
;
ВЫБРАТЬ
Реализация.Ссылка
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
Реализация.Контрагент В
(ВЫБРАТЬ Т.Контрагент ИЗ ВТ_Должники КАК Т)
Типичные ошибки при работе с подзапросами
Большинство проблем с подзапросами в ГДЕ сводится к нескольким повторяющимся сценариям. Знание их заранее экономит время отладки.
Отдельно стоит сказать про NULL. Если подзапрос может вернуть NULL, условие НЕ В ведёт себя неочевидно: строки, для которых сравнение даёт неопределённый результат, исключаются из выборки. Безопасный приём — добавить в подзапрос условие «Поле ЕСТЬ НЕ NULL» или использовать ЕСТЬNULL при сравнении.
Ещё одна ошибка — забыть псевдонимы таблиц внутри подзапроса, когда имена полей совпадают с полями внешнего запроса. Всегда давайте таблицам подзапроса собственные псевдонимы и обращайтесь к полям через них.
Проверка результата и производительности
Перед тем как встроить подзапрос в рабочий код, выполните его отдельно и оцените количество возвращаемых строк. Если подзапрос отдаёт тысячи значений там, где ожидались десятки — проблема в логике отбора, а не в основном запросе.
Как проверить результат
Возвращает ожидаемый набор значений в консоли запросов
Пустой результат или лишние строки — ошибка в условиях подзапроса
Совпадают по типу и составу (для составных полей)
Ссылка сравнивается со строкой, запрос пустой
Запрос отрабатывает за приемлемое время
Долгое выполнение — смотреть план запроса, выносить во временную таблицу
NULL отфильтрованы условием ЕСТЬ НЕ NULL
НЕ В работает непредсказуемо, строки теряются
Для анализа производительности используйте консоль запросов с выводом плана (в режиме толстого клиента или через инструменты разработчика) и технологический журнал с событием DBMSSQL/DBPOSTGRS. Если видите, что СУБД делает полное сканирование большой таблицы ради подзапроса — это сигнал переписать конструкцию через временную таблицу с индексом.
Когда подзапрос не нужен: альтернативы
Часть задач, которые выглядят как «подзапрос в ГДЕ», решается проще. Если отбираете по списку значений, известному заранее, — передавайте список параметром: ГДЕ Поле В (&СписокЗначений). Платформа сама преобразует массив или список в табличный параметр.
Если нужно исключить записи, имеющие связанные строки (антиотбор), надёжнее работает ЛЕВОЕ СОЕДИНЕНИЕ с условием «связанная таблица ЕСТЬ NULL». Этот приём часто быстрее, чем НЕ В, и предсказуемее при NULL.
ВЫБРАТЬ
Номенклатура.Ссылка
ИЗ
Справочник.Номенклатура КАК Номенклатура
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты КАК Продажи
ПО Номенклатура.Ссылка = Продажи.Номенклатура
ГДЕ
Продажи.Номенклатура ЕСТЬ NULL
Как выбрать действие
Частые вопросы
Можно ли использовать подзапрос в ГДЕ с параметрами внутри?
Да, внутри подзапроса доступны все параметры внешнего запроса (&Параметр), а также собственные условия и соединения. Параметр должен быть установлен до выполнения запроса через УстановитьПараметр.
Почему запрос с НЕ В возвращает пустой результат, хотя данные есть?
Наиболее вероятная причина — NULL среди значений подзапроса. Сравнение с NULL даёт неопределённый результат, и условие НЕ В перестаёт срабатывать. Добавьте в подзапрос условие «Поле ЕСТЬ НЕ NULL» и проверьте результат снова.
Что быстрее: подзапрос в ГДЕ или соединение с временной таблицей?
Однозначного ответа нет — зависит от СУБД, объёмов и индексов. На маленьких выборках разница незаметна. На больших таблицах временная таблица с индексом обычно быстрее и стабильнее, потому что её результат материализуется один раз.
Можно ли в подзапросе обращаться к полям внешнего запроса?
Нет, в языке запросов 1С подзапрос в условии ГДЕ не коррелирует с внешней выборкой: он выполняется как самостоятельная выборка. Если нужна корреляция, переписывайте логику через соединение таблиц.
Как отобрать элементы нескольких групп справочника с учётом иерархии?
Используйте В ИЕРАРХИИ с подзапросом, возвращающим нужные группы: ГДЕ Номенклатура В ИЕРАРХИИ (ВЫБРАТЬ Ссылка ИЗ Справочник.Номенклатура ГДЕ Ссылка В (&СписокГрупп)). Проверка иерархии выполнится для каждой группы из списка.
Работает ли подзапрос в ГДЕ в динамических списках и СКД?
Да, язык запросов единый: подзапросы в условии ГДЕ работают в произвольных запросах динамических списков и в наборах данных СКД. Учитывайте только, что динамический список с тяжёлым подзапросом будет медленно обновляться у пользователей.
Что важно запомнить
Подзапрос в ГДЕ пишется через В, НЕ В или В ИЕРАРХИИ с выборкой в скобках. Следите за типами полей и NULL, тяжёлые подзапросы выносите во временные таблицы с индексом, а антиотборы надёжнее делать через ЛЕВОЕ СОЕДИНЕНИЕ с ЕСТЬ NULL.