Написать вложенный запрос 1с

Бесплатная консультация юриста

+7 (800) 301-07-30

Реклама jurik.ru

Работа с данными в 1С:Предприятие 8 немыслима без языка запросов. Одним из самых мощных‚ но требующих осторожности инструментов являются вложенные запросы (подзапросы). Материал ниже основан на актуальных данных сообщества разработчиков‚ курсах (например‚ Курсы-по-1С.рф)‚ документации 1s-up.ru‚ Microsoft Learn (по SQL-аналогам) и стандартах разработки (ССМК) на .

Теория: Что такое вложенный запрос?

Вложенный запрос (подзапрос) — это оператор ВЫБРАТЬ‚ размещенный внутри другого оператора ВЫБРАТЬ‚ ВЫБРАТЬ ... ПОМЕСТИТЬ‚ ОБЪЕДИНИТЬ или в условиях отбора (ГДЕ‚ ИМЕЮЩИЕ)‚ а также в условиях соединения (СОЕДИНЕНИЕ).

Важное уточнение терминологии (обсуждалось на Курсы-по-1С.рф‚ ): Понятия «Подзапрос» и «Вложенный запрос» в контексте 1С часто используются как синонимы. Однако строго говоря‚ подзапрос — это запрос‚ вложенный в условие (ГДЕ/ИМЕЮЩИЕ/СОЕДИНЕНИЕ)‚ а вложенный запрос в широком смысле может использоваться и в списке полей (скалярный подзапрос)‚ и в качестве виртуальной таблицы в секции ИЗ.

Классификация по месту использования

  • В секции ИЗ (Derived Table / Табличное выражение): Результат подзапроса выступает временной таблицей для основного запроса.
  • В списке полей (Скалярный подзапрос): Должен возвращать ровно одно значение (одну строку‚ одно поле). Используется для вычисляемых колонок.
  • В условии ГДЕ / ИМЕЮЩИЕ (Predicate Subquery): Используется с операторами В‚ НЕ В‚ СУЩЕСТВУЕТ‚ НЕ СУЩЕСТВУЕТ‚ >‚ =‚ < и др.
  • В условии соединения (ОН): Позволяет соединять таблицы по сложным вычисляемым условиям. Внимание: Есть серьезные ограничения производительности (см. раздел 4).

Синтаксис и базовые примеры

Синтаксис в 1С близок к SQL-стандарту (Microsoft Learn)‚ но имеет свои особенности (например‚ пакетные запросы и временные таблицы).

Простой вложенный запрос в секции ИЗ

Пример из практики (1s-up.ru‚ ): обращение к выборке по названию таблицы.

ВЫБРАТЬ
 ВложенныйЗапрос.Номенклатура‚
 ВложенныйЗапрос.СуммаОборот КАК ОбъемПродаж
ИЗ
 (ВЫБРАТЬ
 Продажи.Номенклатура КАК Номенклатура‚
 СУММА(Продажи.Сумма) КАК СуммаОборот
 ИЗ
 РегистрНакопления.Продажи КАК Продажи
 ГДЕ
 Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода
 СГРУППИРОВАТЬ ПО
 Продажи.Номенклатура) КАК ВложенныйЗапрос
ГДЕ
 ВложенныйЗапрос.СуммаОборот > 100000

Пояснение: Внутри скобок формируется промежуточный набор данных (агрегат по продажам)‚ которому присваивается псевдоним ВложенныйЗапрос. Внешний запрос фильтрует уже готовый результат.

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

Получение последней цены номенклатуры на момент продажи.

ВЫБРАТЬ
 Продажи.Номенклатура‚
 Продажи.Количество‚
 (ВЫБРАТЬ ПЕРВЫЕ 1
 Цены.Цена
 ИЗ
 РегистрСведений.ЦеныНоменклатуры КАК Цены
 ГДЕ
 Цены.Номенклатура = Продажи.Номенклатура
 И Цены.Период <= Продажи.Период
 УПОРЯДОЧИТЬ ПО
 Цены.Период УБЫВ) КАК ПоследняяЦена
ИЗ
 Документ.РеализацияТоваровУслуг КАК Продажи

Подзапрос в условии ГДЕ (Оператор В)

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

ВЫБРАТЬ
 Клиенты.Наименование
ИЗ
 Справочник.Контрагенты КАК Клиенты
ГДЕ
 Клиенты.Ссылка В
 (ВЫБРАТЬ РАЗЛИЧНЫЕ
 Продажи.Контрагент
 ИЗ
 РегистрНакопления.Продажи КАК Продажи
 ГДЕ
 Продажи.Период МЕЖДУ &НачалоМесяца И &КонецМесяца)

Продвинутые техники: Вложенные таблицы и Пакетные запросы

Язык запросов 1С позволяет не просто вкладывать SELECT в SELECT‚ а управлять потоком данных через временные таблицы и пакеты. Это часто эффективнее чистых вложенных запросов.

Временные таблицы vs Вложенные запросы

Согласно обсуждениям на 1s-up.ru и стандартам ССМК: Консоль запросов позволяет выполнять выделенные фрагменты. Временные таблицы (ПОМЕСТИТЬ ВТ_Имя) имеют критическое преимущество:

  • Могут передаваться между запросами в рамках одного пакета.
  • Могут повторно использоваться многократно.
  • Позволяют создавать индексы (ИНДЕКСЫ ПО (Поле1‚ Поле2)) для ускорения последующих соединений.

Пример замены вложенного запроса на временную таблицу (Пакетный запрос):

// Запрос 1: Подготовка агрегата во временную таблицу
ВЫБРАТЬ
 Продажи.Номенклатура‚
 СУММА(Продажи.Сумма) КАК ОбъемПродаж
ПОМЕСТИТЬ ВТ_ПродажиПоНоменклатуре
ИЗ
 РегистрНакопления.Продажи КАК Продажи
ГДЕ
 Продажи.Период МЕЖДУ &Начало И &Конец
СГРУППИРОВАТЬ ПО
 Продажи.Номенклатура
ИНДЕКСЫ ПО
 Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
// Запрос 2: Использование временной таблицы (можно вызвать отдельно в консоли)
ВЫБРАТЬ
 Ном.Наименование‚
 ВТ_ПродажиПоНоменклатуре.ОбъемПродаж
ИЗ
 ВТ_ПродажиПоНоменклатуре КАК ВТ

 ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном ПО Ном.Ссылка = ВТ.Номенклатура
ГДЕ
 ВТ.ОбъемПродаж > 100000

Работа с вложенными таблицами в коде (1С:Программно)

Как отмечалось на 1s-up.ru‚ результат запроса‚ содержащего вложенные таблицы (или иерархию)‚ можно обрабатывать программно:

Результат = Запрос.Выполнить;
// Получаем выборку по "внешней" таблице
Выборка = Результат.Выбрать; 

Пока Выборка.Следующий Цикл
 // Если в запросе была вложенная таблица (например‚ через ОБЪЕДИНИТЬ или структуру)‚
 // к ней можно обратиться через точку‚ если она представлена как поле-ТаблицаЗначений
 // Или‚ если это результат пакета с несколькими таблицами:
 ВложеннаяТаблица = Результат["ИмяВложеннойТаблицы"].Выбрать; 
 // Обработка вложенной выборки...
КонецЦикла;

Оптимизация и «Подводные камни» (Best Practices)

Это самый важный раздел. Существует устойчивое мнение экспертов (проверено ‚ источники: профильные блоги‚ ССМК)‚ что вложенные запросы в сложных конструкциях выполняются платформой 1С нерационально‚ требуя больших ресурсов (RAM‚ CPU‚ TempDB) и времени‚ нежели альтернативы.

Главное правило ССМК: Не использовать вложенные запросы в условиях соединения

Источник: «Не использовать вложенные запросы в условиях соединения» :: Язык запросов 1С:Предприятия (Стандарты разработки‚ ).

⛔ Антипаттерн: Подзапрос в ОН (Условии соединения)

// ПЛОХО: Подзапрос внутри условия соединения
ВЫБРАТЬ
 Т1.Поле1
ИЗ
 Таблица1 КАК Т1
 ЛЕВОЕ СОЕДИНЕНИЕ Таблица2 КАК Т2
 ПО Т1.Ключ = Т2.Ключ
 И Т2.Поле В (ВЫБРАТЬ Поле3 ИЗ Таблица3 ГДЕ Условие) // ОЧЕНЬ МЕДЛЕННО

Почему это плохо: Оптимизатор СУБД (MS SQL / PostgreSQL) часто не может «развернуть» такой подзапрос в эффективный план (Hash Join / Merge Join)‚ переходя к Nested Loops с повторным выполнением подзапроса для каждой строки.

✅ Хорошо: Предварительное материализация через Временную Таблицу

// ХОРОШО: Пакетный запрос с ВТ
ВЫБРАТЬ Поле3 ПОМЕСТИТЬ ВТ_Фильтр ИЗ Таблица3 ГДЕ Условие ИНДЕКСЫ ПО Поле3;

ВЫБРАТЬ Т1.Поле1
ИЗ Таблица1 КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ Таблица2 КАК Т2 ПО Т1.Ключ = Т2.Ключ
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Фильтр КАК Ф ПО Т2.Поле = Ф.Поле3

Оператор ОБЪЕДИНИТЬ и «Галка без дублей»

Вопрос с Курсы-по-1С.рф: «Если при объединении запросов поставить галку «Без дублей» (DISTINCT) на первый запрос‚ это повлияет на что-нибудь?»

Ответ: Да‚ критически. ОБЪЕДИНИТЬ (UNION) по умолчанию убирает дубликаты (как UNION DISTINCT в SQL)‚ что требует дорогой операции сортировки/хеширования всего результата. ОБЪЕДИНИТЬ ВСЕ (UNION ALL) просто склеивает потоки данных.

  • Если вы ставите «Без дублей» (DISTINCT) на первый запрос пакета перед UNION — вы тратите ресурсы на дедупликацию промежуточного результата‚ который все равно пройдет дедупликацию при финальном UNION (если не UNION ALL).
  • Рекомендация: Всегда используйте ОБЪЕДИНИТЬ ВСЕ‚ если бизнес-логика допускает дубликаты или вы гарантируете их отсутствие на уровне данных. Явный РАЗЛИЧНЫЕ (DISTINCT) ставьте только в самом внешнем запросе или в финальном шаге.

Коррелированные подзапросы (Correlated Subqueries)

Подзапрос‚ ссылающийся на поля внешнего запроса (как пример в п. 2.2 с Цены.Номенклатура = Продажи.Номенклатура).

  • Выполняются для каждой строки внешнего запроса (Row-by-agonizing-row, RBAR).
  • На больших выборках (тысячи строк) — гарантированный таймаут или зависание.
  • Лечение: Замена на ЛЕВОЕ СОЕДИНЕНИЕ с предварительно агрегированной временной таблицей (см. п. 3.1).

Чек-лист разработчика: Когда что применять

Сценарий Рекомендуемый подход Комментарий
Простая фильтрация (список значений < 1000) ГДЕ Поле В (Подзапрос) Читаемо‚ оптимизатор справляется хорошо.
Сложная агрегация перед соединением Временная таблица (ПОМЕСТИТЬ ВТ_…) в пакете Контроль индексов‚ повторное использование‚ план выполнения предсказуем;
Получение одного значения на строку (последняя цена‚ остаток) ЛЕВОЕ СОЕДИНЕНИЕ с ВТ / Виртуальной таблицей среза последних Избегайте скалярных подзапросов в списке полей на больших объемах.
Условие соединения зависит от подзапроса Вынести подзапрос в ВТ‚ соединить ВТ Строгое требование ССМК (п. 4.1).
Рекурсивные запросы / Иерархии Иерархические запросы (ИЕРАРХИЯ) или ВТ в цикле (на сервере) Вложенные запросы не поддерживают рекурсию напрямую.

Отладка в Консоли Запросов

Совет от 1s-up.ru : Консоль запросов 1С позволяет выполнять выделенную часть текста;

  1. Выделите код вложенного запроса (внутри скобок после ИЗ или в ГДЕ).
  2. Нажмите «Выполнить» (F5).
  3. Анализируйте результат и план запроса (вкладка «План») изолированно.
  4. Так вы локализуете узкое место: тормозит ли сам подзапрос или его соединение с внешней таблицей.

Резюме

Вложенные запросы в 1С — это стандартный синтаксический сахар‚ который делает код компактным. Однако на платформе 1С:Предприятие 8.3 (актуально на ) производительность сложных отчетов почти всегда выигрывает от рефакторинга вложенных запросов в пакеты с временными таблицами;

Главные выводы:

  • Используйте вложенные запросы в ИЗ и ГДЕ для простых и средних задач.
  • Категорически избегайте вложенных запросов в условиях СОЕДИНЕНИЕ ... ПО (ON clause).
  • Заменяйте коррелированные подзапросы и скалярные подзапросы в списке полей на ЛЕВОЕ СОЕДИНЕНИЕ с предварительно рассчитанными Временными Таблицами.
  • Используйте ОБЪЕДИНИТЬ ВСЕ вместо ОБЪЕДИНИТЬ везде‚ где это допустимо логикой.
  • Отлаживайте части запроса отдельно в Консоли Запросов.

Статья подготовлена на основе открытых источников: Курсы-по-1С.рф‚ 1s-up.ru‚ Документация 1С (ССМК)‚ Microsoft Learn‚ профильные блоги оптимизации. Дата актуализации: .