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