Как одним запросом получить данные табличных частей разных видов документов с группировкой для Построителя отчета?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок в 1С часто возникает задача: выбрать строки табличных частей сразу из нескольких разных документов за определенный период и вывести результат в виде иерархической структуры: Дата поступления → Вид документа → Документ → Строки табличной части. Разберем типичные ошибки при реализации такого запроса и рассмотрим правильную архитектуру решения с использованием ПостроительОтчета.

Анализ типичных ошибок в запросе

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

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

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

Разберем по шагам, как правильно составить единый запрос:

  1. Прямой запрос к табличной части: обращаемся напрямую к табличной части Документ.ИмяДокумента.Товары каждого нужного вида документа.
  2. Фильтрация по дате: накладываем условие на дату шапки через реквизит ссылки: ГДЕ ТабЧасть.Ссылка.ДатаПоступления МЕЖДУ &ДатаНач И &ДатаКон.
  3. Идентификация вида документа: в каждом подзапросе создаем строковую константу или синоним (например, "Заявка на перемещение" КАК ВидДокумента), чтобы на уровне группировок различать типы документов.
  4. Объединение выборок: объединяем результаты по разным видам документов с помощью оператора ОБЪЕДИНИТЬ ВСЕ (оператор ОБЪЕДИНИТЬ без ВСЕ выполнять не рекомендуется, так как он делает неявный DISTINCT, замедляя выполнение).
  5. Секция итогов: задаем иерархию группировок через ИТОГИ ... ПО.

Посмотрим на пример правильного текста запроса:


ВЫБРАТЬ
    "Заявка на перемещение" КАК ВидДокумента,
    ЗаявкаТовары.Ссылка.рар_ДатаПоступления КАК ДатаПоступления,
    ЗаявкаТовары.Ссылка КАК Документ,
    ЗаявкаТовары.Номенклатура КАК Номенклатура,
    ЗаявкаТовары.СерияНоменклатуры КАК СерияНоменклатуры,
    ЗаявкаТовары.Количество КАК Количество,
    ЗаявкаТовары.ДокументРезерва КАК ДокументРезерва
ИЗ
    Документ.рар_ЗаявкаНаВнутреннееПеремещениеТоваров.Товары КАК ЗаявкаТовары
ГДЕ
    ЗаявкаТовары.Ссылка.рар_ДатаПоступления МЕЖДУ &ДатаНач И &ДатаКон

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
    "Поступление товаров" КАК ВидДокумента,
    ПоступлениеТовары.Ссылка.рар_ДатаПоступления КАК ДатаПоступления,
    ПоступлениеТовары.Ссылка КАК Документ,
    ПоступлениеТовары.Номенклатура КАК Номенклатура,
    ПоступлениеТовары.СерияНоменклатуры КАК СерияНоменклатуры,
    ПоступлениеТовары.Количество КАК Количество,
    ПоступлениеТовары.ДокументРезерва КАК ДокументРезерва
ИЗ
    Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТовары
ГДЕ
    ПоступлениеТовары.Ссылка.рар_ДатаПоступления МЕЖДУ &ДатаНач И &ДатаКон

ИТОГИ
    СУММА(Количество)
ПО
    ДатаПоступления,
    ВидДокумента,
    Документ

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

Если результат запроса обрабатывается вручную в коде, обход дерева итогов выполняется последовательно через вызовы метода Выбрать() с нужными типами обхода:


Запрос = Новый Запрос;
Запрос.Текст = ТекстЗапроса;
Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
Запрос.УстановитьПараметр("ДатаКон", ДатаКон);

РезультатЗапроса = Запрос.Выполнить();

// 1 уровень - Дата поступления
ВыборкаДата = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаДата.Следующий() Цикл
    
    // 2 уровень - Вид документа
    ВыборкаВид = ВыборкаДата.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    Пока ВыборкаВид.Следующий() Цикл
        
        // 3 уровень - Ссылка на документ
        ВыборкаДокумент = ВыборкаВид.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
        Пока ВыборкаДокумент.Следующий() Цикл
            
            // 4 уровень - Детальные записи (строки табличной части)
            ВыборкаСтроки = ВыборкаДокумент.Выбрать(ОбходРезультатаЗапроса.Прямой);
            Пока ВыборкаСтроки.Следующий() Цикл
                // Обработка полей детальной строки
            КонецЦикла;
            
        КонецЦикла;
        
    КонецЦикла;
    
КонецЦикла;

Использование запроса в объекте «ПостроительОтчета»

При передаче запроса в ПостроительОтчета можно использовать расширения языка запросов (конструкции в фигурных скобках {}). Построитель отчета способен самостоятельно управлять итогами на основании измерений строк.

Рассмотрим, как инициализировать построитель отчета и передать параметры:


Построитель = Новый ПостроительОтчета;
Построитель.Текст = 
"ВЫБРАТЬ
|	""Заявка на перемещение"" КАК ВидДокумента,
|	ЗаявкаТовары.Ссылка.рар_ДатаПоступления КАК ДатаПоступления,
|	ЗаявкаТовары.Ссылка КАК Документ,
|	ЗаявкаТовары.Номенклатура КАК Номенклатура,
|	ЗаявкаТовары.Количество КАК Количество
|{ВЫБРАТЬ
|	ВидДокумента,
|	ДатаПоступления,
|	Документ.*,
|	Номенклатура.*,
|	Количество}
|ИЗ
|	Документ.рар_ЗаявкаНаВнутреннееПеремещениеТоваров.Товары КАК ЗаявкаТовары
|{ГДЕ
|	ЗаявкаТовары.Ссылка.рар_ДатаПоступления МЕЖДУ &ДатаНач И &ДатаКон}
|
|ОБЪЕДИНИТЬ ВСЕ
|
|ВЫБРАТЬ
|	""Поступление товаров"",
|	ПоступлениеТовары.Ссылка.рар_ДатаПоступления,
|	ПоступлениеТовары.Ссылка,
|	ПоступлениеТовары.Номенклатура,
|	ПоступлениеТовары.Количество
|{ВЫБРАТЬ
|	ВидДокумента,
|	ДатаПоступления,
|	Документ.*,
|	Номенклатура.*,
|	Количество}
|ИЗ
|	Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТовары
|{ГДЕ
|	ПоступлениеТовары.Ссылка.рар_ДатаПоступления МЕЖДУ &ДатаНач И &ДатаКон}";

Построитель.Параметры.Вставить("ДатаНач", НачалоДня(ДатаНач));
Построитель.Параметры.Вставить("ДатаКон", КонецДня(ДатаКон));

// Настройка группировок строк построителя
Построитель.ИзмеренияСтроки.Очистить();
Построитель.ИзмеренияСтроки.Добавить("ДатаПоступления");
Построитель.ИзмеренияСтроки.Добавить("ВидДокумента");
Построитель.ИзмеренияСтроки.Добавить("Документ");

// Формирование вывода
Построитель.Выполнить();
Построитель.Вывести(ЭлементыФормы.ПолеТабличногоДокумента);

Важные правила при работе с Построителем отчета и ОБЪЕДИНИТЬ ВСЕ:

  1. Количество и типы выгружаемых полей во всех частях запроса, объединяемых через ОБЪЕДИНИТЬ ВСЕ, должны строго совпадать.
  2. Псевдонимы полей берутся из первого запроса объединения.
  3. Секции расширения {ГДЕ} и {ВЫБРАТЬ} должны корректно дублироваться во всех ветках объединения, чтобы пользовательские отборы применялись ко всем типам документов одинаково.
← На главную