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

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

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

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

Вложенный запрос в условии ГДЕ — приём, при котором результат одной выборки используется для фильтрации другой прямо в тексте запроса 1С. Вместо того чтобы сначала получить список контрагентов, перенести его в массив и передать параметром, разработчик пишет конструкцию вида «ГДЕ Контрагент В (ВЫБРАТЬ ...)» и получает отфильтрованные данные одним обращением к базе. Это сокращает код, уменьшает число обращений к СУБД и делает логику прозрачнее.

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

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

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

Базовый синтаксис выглядит так:

ВЫБРАТЬ

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

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

ИЗ

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

ГДЕ

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

(ВЫБРАТЬ

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

ИЗ

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

ГДЕ

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

Здесь внутренний запрос возвращает ссылки контрагентов из нужного региона, а внешний отбирает документы только по этим контрагентам. Ключевое требование: подзапрос в операторе В должен возвращать ровно одно поле, и его тип должен быть сравним с типом проверяемого поля. Если выбрать два поля, конструктор запроса выдаст ошибку ещё на этапе проверки текста.

Оператор В и его ограничения

Оператор В — основной способ применения подзапроса в ГДЕ. Он проверяет, входит ли значение поля в множество, которое вернула внутренняя выборка. Отрицание записывается как НЕ ... В — в языке запросов 1С отрицание ставится перед всем выражением: «ГДЕ НЕ Поле В (...)».

Есть несколько ограничений, о которых нужно помнить:

  • подзапрос обязан возвращать одно поле — список из двух и более полей недопустим;
  • тип возвращаемого поля должен совпадать или приводиться к типу сравниваемого поля; сравнение ссылки с числом приведёт к ошибке;
  • подзапрос не может содержать итоги (ИТОГИ) и упорядочивание, не имеющее смысла для множества значений;
  • внутри подзапроса допустимы свои параметры (&Параметр), соединения и группировки.

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

Фильтрация по иерархии: оператор В ИЕРАРХИИ

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

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

ВЫБРАТЬ

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

Товары.Количество

ИЗ

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

ГДЕ

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

(ВЫБРАТЬ

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

ИЗ

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

ГДЕ

Номенклатура.ЭтоГруппа

И Номенклатура.Наименование ПОДОБНО &МаскаГруппы)

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

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

Отдельный сценарий — подзапрос, возвращающий одно значение (агрегат), которое сравнивается с полем через обычные операторы =, >, <. Например, отбор документов с суммой выше средней по периоду:

ВЫБРАТЬ

Продажи.Документ,

Продажи.СуммаОборот

ИЗ

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

ГДЕ

Продажи.СуммаОборот >

(ВЫБРАТЬ

СРЕДНЕЕ(П.СуммаОборот)

ИЗ

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

Безопасность такого приёма зависит от гарантии единственности результата. Агрегатные функции без группировки (СРЕДНЕЕ, СУММА, МАКСИМУМ) всегда возвращают одну строку, поэтому сравнение корректно. Если же подзапрос может вернуть несколько строк, используйте В, а не знак равенства, иначе поведение будет зависеть от платформы и СУБД, а результат — непредсказуемым.

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

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

Признаки того, что конструкцию стоит переработать:

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

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

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

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

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

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

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

Соединение (СОЕДИНЕНИЕ)

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

Параметр-список из кода

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

Пошаговый порядок написания и проверки

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

Порядок действий
  1. 1
    Напишите и выполните внутренний запрос отдельно. Убедитесь, что он возвращает одно поле нужного типа и осмысленный набор значений.
  2. 2
    Проверьте внутренний запрос на граничных данных: пустой результат, одна строка, большой объём.
  3. 3
    Вставьте подзапрос в условие ГДЕ внешнего запроса через оператор В (или В ИЕРАРХИИ для справочников с иерархией).
  4. 4
    Убедитесь, что типы полей совместимы: ссылка со ссылкой, число с числом. При необходимости используйте ВЫРАЗИТЬ для приведения типа.
  5. 5
    Выполните весь запрос в консоли запросов и сверьте количество строк с ожидаемым: отдельно посчитайте записи без фильтра и с фильтром.
  6. 6
    Замерьте время выполнения. Если оно неприемлемо, перепишите через временную таблицу или соединение и сравните результат — наборы строк должны совпасть.

Консоль запросов (внешняя обработка или встроенный конструктор в конфигураторе) — основной инструмент проверки. Она позволяет выполнить фрагмент текста отдельно, подставить параметры и увидеть результат до того, как запрос попадёт в модуль.

Типичные ошибки и как их избежать

Типичные ошибки
01
Выбор двух полей в подзапросе оператора В — текст не проходит проверку, запрос не выполняется. Оставьте в подзапросе одно поле.
02
Сравнение через «=» с подзапросом, возвращающим несколько строк, — результат непредсказуем. Используйте В или гарантируйте единственность агрегатом.
03
Несовпадение типов: поле типа Строка сравнивается с результатом подзапроса, возвращающим ссылку. Приводите тип через ВЫРАЗИТЬ или исправьте выборку.
04
Игнорирование пустого результата подзапроса — внешний запрос молча возвращает ноль строк, и причину ищут в другом месте. Проверяйте подзапрос отдельно.
05
Повторение одного и того же тяжёлого подзапроса в нескольких условиях — многократное выполнение и падение скорости. Вынесите его во временную таблицу.
06
Забытое отрицание: пишут «Поле НЕ В», тогда как правильный синтаксис — «НЕ Поле В (...)». Запрос не компилируется.

Отдельная группа проблем — NULL. Если поле внешней таблицы содержит NULL (например, после левого соединения), условие «Поле В (...)» для таких строк даст не ИСТИНА и не ЛОЖЬ, а неопределённость, и строка будет отброшена. Если NULL-значения должны попадать в результат, добавьте в условие явную проверку: «ГДЕ (Поле В (...) ИЛИ Поле ЕСТЬ NULL)».

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

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

Число строк результата
Норма

Совпадает с ручным подсчётом по отдельно выполненным запросам

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

Строк меньше, чем ожидалось, без объяснимой причины — проверьте NULL и пустой подзапрос

Тип поля подзапроса
Норма

Совпадает с типом сравниваемого поля

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

Ошибка несовместимости типов при выполнении

Время выполнения
Норма

Сопоставимо с вариантом через временную таблицу

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

В разы медленнее — пересмотрите конструкцию

Поведение на пустом подзапросе
Норма

Понятно и задокументировано: ноль строк — ожидаемый исход

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

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

Практические сценарии из учётных задач

Отбор документов по признаку связанного объекта. Например, найти все поступления от поставщиков, у которых есть незакрытые долги: подзапрос выбирает контрагентов с остатком по регистру расчётов, внешний запрос отбирает документы по этим контрагентам. Это типичная задача, где подзапрос в ГДЕ заменяет двухэтапную обработку в коде.

Анти-отбор через отрицание. Найти номенклатуру, по которой не было продаж за период: «ГДЕ НЕ Номенклатура.Ссылка В (ВЫБРАТЬ Продажи.Номенклатура ...)». Учтите, что на больших таблицах такая конструкция может выполняться медленно; альтернатива — левое соединение с проверкой на NULL, которое оптимизатор часто обрабатывает эффективнее.

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

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

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

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

Что вернёт запрос, если подзапрос в ГДЕ ничего не нашёл?

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

Чем отличается В от В ИЕРАРХИИ?

В проверяет точное вхождение значения в список. В ИЕРАРХИИ дополнительно учитывает всех потомков: если в списке группа справочника, условие сработает для всех элементов этой группы и её подгрупп любой глубины.

Когда лучше заменить подзапрос временной таблицей?

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

Почему строки со значением NULL не попадают в результат?

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

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

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

Итог

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

Вложенный запрос в ГДЕ пишется через оператор В и должен возвращать одно поле совместимого типа. Для иерархии используйте В ИЕРАРХИИ, для сравнения с агрегатом — скалярный подзапрос. Проверяйте подзапрос отдельно, помните про NULL и пустые результаты, а на больших объёмах сравнивайте скорость с вариантом через временную таблицу.