Зачем нужен вложенный запрос при выборке из табличной части в 1С?

Программист IT и автоматизация бизнеса
← На главную

При работе с запросами в платформах 1С:Предприятие разработчики часто сталкиваются с конструкциями, в которых первичная выборческая логика помещена во вложенный запрос, а во внешнем запросе происходит окончательная обработка полей. На первый взгляд может показаться, что такую выборку можно написать в один простой запрос без вложенных секций. Однако у подобного подхода есть важные технические и архитектурные причины. Проанализируем ситуации, когда использование вложенного запроса оправдано, и разберем его ключевые задачи.

1. Свертка (группировка) одинаковых строк табличной части

Один из наиболее распространенных сценариев использования вложенного запроса — это объединение повторяющихся строк табличной части документа. Если в документе (например, в «Заказе покупателя») одна и та же номенклатура с одинаковой ценой и скидками указана несколькими строками, предстоит сгруппировать эти данные перед дальнейшей обработкой или выводом в печатную форму.

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


ВЫБРАТЬ
    ЗаказПокупателя.Номенклатура КАК Номенклатура,
    ЗаказПокупателя.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    ЗаказПокупателя.ПроцентСкидкиНаценки КАК ПроцентСкидкиНаценки,
    ЗаказПокупателя.ПроцентАвтоматическихСкидок КАК ПроцентАвтоматическихСкидок,
    ЗаказПокупателя.Цена КАК Цена,
    СУММА(ЗаказПокупателя.Количество) КАК Количество,
    СУММА(ЗаказПокупателя.Сумма) КАК Сумма,
    СУММА(ЗаказПокупателя.СуммаНДС) КАК СуммаНДС,
    ЗаказПокупателя.ХарактеристикаНоменклатуры КАК Характеристика,
    МИНИМУМ(ЗаказПокупателя.НомерСтроки) КАК НомерСтроки
ИЗ
    Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателя
ГДЕ
    ЗаказПокупателя.Ссылка = &ТекущийДокумент
СГРУППИРОВАТЬ ПО
    ЗаказПокупателя.Номенклатура,
    ЗаказПокупателя.ЕдиницаИзмерения,
    ЗаказПокупателя.ПроцентСкидкиНаценки,
    ЗаказПокупателя.ПроцентАвтоматическихСкидок,
    ЗаказПокупателя.Цена,
    ЗаказПокупателя.ХарактеристикаНоменклатуры

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

2. Оптимизация обращений к реквизитам через точку

Рассмотрим обращение к полям связанного справочника, таким как Номенклатура.НаименованиеПолное, Номенклатура.Код или Номенклатура.Артикул. Если производить разыменование ссылок (обращение через точку) до выполнения группировки, СУБД будет вынуждена выполнять неявные соединения с таблицей справочника для каждой строки табличной части документа еще до того, как они будут свернуты.

Рассмотрим разницу в производительности на примере:

  1. Если в документе содержатся 1000 строк, но у них всего 10 уникальных позиций номенклатуры, обращение через точку в одноуровневом запросе с группировкой заставит СУБД обрабатывать 1000 записей справочника.
  2. Если сначала сгруппировать данные во вложенном запросе (получив всего 10 строк), а затем во внешнем запросе вытащить реквизиты через точку, СУБД выполнит неявное соединение только для 10 итоговых строк. Это существенно снижает нагрузку на базу данных.

3. Получение синтаксически недопустимых полей и вычисления после группировки

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

Виртуальное поле `.Представление` — представление объекта нельзя использовать в блоке группировки. Поэтому выражение ВложенныйЗапрос.ЕдиницаИзмерения.Представление выносится во внешний запрос уже после свёртки строк.

Сложение вычисляемых полей — вычисление суммарного процента скидки (ПроцентСкидкиНаценки + ПроцентАвтоматическихСкидок) логично и безопасно выполнять только над уже сгруппированными строками во внешнем запросе.

Ограничение типов (`ВЫРАЗИТЬ`) — если реквизит (например, НаименованиеПолное) имеет неограниченную длину, над ним нельзя выполнять операции группировки. Выражение ВЫРАЗИТЬ(ВложенныйЗапрос.Номенклатура.НаименованиеПолное КАК Строка(1000)) во внешнем запросе предотвращает ошибки работы СУБД.

4. Подготовка структуры данных под объединение табличных частей

В типовых конфигурациях 1С запросы печатных форм часто строятся по универсальному шаблону, позволяющему объединять несколько табличных частей (например, «Товары» и «Услуги»).

Для этого в запрос добавляются фиктивные поля, такие как NULL КАК Серия и служебные метки 0 КАК Метка. Во внешнем запросе или через ОБЪЕДИНИТЬ ВСЕ эти структуры сводятся к единообразному виду, что позволяет вывести товары и услуги в один документ с правильным типом колонок и последовательностью строк.

Как оптимизировать такой код по стандартам 1С?

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

Рекомендуется переписать подобный запрос с использованием временных таблиц:


ВЫБРАТЬ
    ЗаказПокупателя.Номенклатура КАК Номенклатура,
    ЗаказПокупателя.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
    ЕСТЬNULL(ЗаказПокупателя.ПроцентСкидкиНаценки, 0) КАК ПроцентСкидкиНаценки,
    ЕСТЬNULL(ЗаказПокупателя.ПроцентАвтоматическихСкидок, 0) КАК ПроцентАвтоматическихСкидок,
    ЗаказПокупателя.Цена КАК Цена,
    СУММА(ЗаказПокупателя.Количество) КАК Количество,
    СУММА(ЗаказПокупателя.Сумма) КАК Сумма,
    СУММА(ЗаказПокупателя.СуммаНДС) КАК СуммаНДС,
    ЗаказПокупателя.ХарактеристикаНоменклатуры КАК Характеристика,
    МИНИМУМ(ЗаказПокупателя.НомерСтроки) КАК НомерСтроки
ПОМЕСТИТЬ ВТ_СвернутыеТовары
ИЗ
    Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателя
ГДЕ
    ЗаказПокупателя.Ссылка = &ТекущийДокумент
СГРУППИРОВАТЬ ПО
    ЗаказПокупателя.Номенклатура,
    ЗаказПокупателя.ЕдиницаИзмерения,
    ЗаказПокупателя.ПроцентСкидкиНаценки,
    ЗаказПокупателя.ПроцентАвтоматическихСкидок,
    ЗаказПокупателя.Цена,
    ЗаказПокупателя.ХарактеристикаНоменклатуры;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    ВЫРАЗИТЬ(Товары.Номенклатура.НаименованиеПолное КАК Строка(1000)) КАК НаименованиеПолное,
    Товары.Номенклатура.Код КАК Код,
    Товары.Номенклатура.Артикул КАК Артикул,
    Товары.Количество КАК Количество,
    Товары.ЕдиницаИзмерения.Представление КАК ЕдиницаИзмерения,
    Товары.ПроцентСкидкиНаценки + Товары.ПроцентАвтоматическихСкидок КАК Скидка,
    Товары.Цена КАК Цена,
    Товары.Сумма КАК Сумма,
    Товары.СуммаНДС КАК СуммаНДС,
    Товары.Характеристика КАК Характеристика,
    NULL КАК Серия,
    Товары.НомерСтроки КАК НомерСтроки
ИЗ
    ВТ_СвернутыеТовары КАК Товары
УПОРЯДОЧИТЬ ПО
    Товары.НомерСтроки

Перенос логики во временную таблицу ВТ_СвернутыеТовары позволяет платформе корректно рассчитать статистику по временной таблице, а СУБД построит наиболее эффективный план выполнения запроса.

← На главную