Задача перевода "плоской" информации в "иерархическую" встает перед разработчиком 1С достаточно часто. В качестве примера такой задачи можно привести случай, когда мы имеем таблицу с данными о контрагентах и проданных им товарах, из данных которой нам нужно сформировать документы реализации. Каждая реализация должна объединять в себе строки по одному контрагенту, и все эти строки должны формировать табличную часть товаров документа.
Такая задача решается легко, путем использования ключевого слова ИТОГИ в запросе:
ВЫБРАТЬ
ТаблицаТоваров.Контрагент КАК Контрагент,
ТаблицаТоваров.Номенклатура КАК Номенклатура,
ТаблицаТоваров.Количество КАК Количество,
ТаблицаТоваров.Сумма КАК Сумма
ИЗ
&ТаблицаТоваров КАК ТаблицаТоваров
ИТОГИ
СУММА(Сумма)
ПО
Контрагент
Затем результат запроса обходим по группировкам и создаем документы:
ВыборкаПоКонтрагентам = Запрос.Выполнить.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоКонтрагентам.Следующий() Цикл
// Заполнение шапки
ДетальныеЗаписи = ВыборкаПоКонтрагентам.Выбрать();
Пока ДетальныеЗаписи.Следующий() Цикл
// Заполнение ТЧ товаров
КонецЦикла;
КонецЦикла;
Усложним задачу, добавив в исходную таблицу колонку "Договор".
Теперь каждая отдельная реализация должна объединять строки с одинаковым набором значений Контрагент-Договор. Для того чтобы это учесть, можно добавить в запрос итоги по полю договор. При обходе результата запроса у нас появится еще один вложенный цикл.
ВыборкаПоКонтрагентам = Запрос.Выполнить.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоКонтрагентам.Следующий() Цикл
ВыборкаПоДоговорам = ВыборкаПоКонтрагентам.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоДоговорам.Следующий() Цикл
// Заполнение шапки
ДетальныеЗаписи = ВыборкаПоДоговорам.Выбрать();
Пока ДетальныеЗаписи.Следующий() Цикл
// Заполнение ТЧ товаров
КонецЦикла;
КонецЦикла;
КонецЦикла;
Добавим в таблицу еще пару полей. Например, "Организация" и "Грузоотправитель". Реализация теперь создается на каждый набор Организация-Контрагент-Грузоотправитель-Договор. Если действовать тем же методом, добавляя итоговые поля в запрос, при обработке результата мы получим цикл пятого уровня вложенности:
ВыборкаПоОрганизациям = Запрос.Выполнить.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоОрганизациям.Следующий() Цикл
ВыборкаПоКонтрагентам = ВыборкаПоОрганизациям.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоКонтрагентам.Следующий() Цикл
ВыборкаПоГрузополучателям = ВыборкаПоКонтрагентам.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоГрузополучателям.Следующий() Цикл
ВыборкаПоДоговорам = ВыборкаПоГрузополучателям.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоДоговорам.Следующий() Цикл
// Заполнение шапки
ДетальныеЗаписи = ВыборкаПоДоговорам.Выбрать();
Пока ДетальныеЗаписи.Следующий() Цикл
// Заполнение ТЧ товаров
КонецЦикла;
КонецЦикла;
КонецЦикла;
КонецЦикла;
КонецЦикла;
Вид этой конструкции вызывает, мягко говоря, негативные эмоции.
А ведь группировочных полей может быть еще больше...
Для того чтобы избежать матрешки из циклов, нужно присвоить каждой связке Организация-Контрагент-Грузоотправитель-Договор какой-то идентификатор и производить группировку по всему набору, а не по каждому полю в отдельности. В рамках языка запросов это можно сделать при помощи функции АВТОНОМЕРЗАПИСИ. Для нашего примера запрос должен выглядеть так:
ВЫБРАТЬ
ТаблицаТоваров.Организация КАК Организация,
ТаблицаТоваров.Контрагент КАК Контрагент,
ТаблицаТоваров.Грузополучатель КАК Грузополучатель,
ТаблицаТоваров.Договор КАК Договор,
ТаблицаТоваров.Номенклатура КАК Номенклатура,
ТаблицаТоваров.Количество КАК Количество,
ТаблицаТоваров.Сумма КАК Сумма
ПОМЕСТИТЬ ВТ_ИсходнаяТаблица
ИЗ
&ТаблицаТоваров КАК ТаблицаТоваров
ИНДЕКСИРОВАТЬ ПО
Организация,
Контрагент,
Грузополучатель,
Договор
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВТ_ИсходнаяТаблица.Организация КАК Организация,
ВТ_ИсходнаяТаблица.Контрагент КАК Контрагент,
ВТ_ИсходнаяТаблица.Грузополучатель КАК Грузополучатель,
ВТ_ИсходнаяТаблица.Договор КАК Договор,
АВТОНОМЕРЗАПИСИ() КАК ИдентификаторНабора
ПОМЕСТИТЬ ВТ_РеквизитыШапки
ИЗ
ВТ_ИсходнаяТаблица КАК ВТ_ИсходнаяТаблица
ИНДЕКСИРОВАТЬ ПО
Организация,
Грузополучатель,
Контрагент,
Договор
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_РеквизитыШапки.ИдентификаторНабора КАК ИдентификаторНабора,
ВТ_РеквизитыШапки.Организация КАК Организация,
ВТ_РеквизитыШапки.Контрагент КАК Контрагент,
ВТ_РеквизитыШапки.Грузополучатель КАК Грузополучатель,
ВТ_РеквизитыШапки.Договор КАК Договор,
ВТ_ИсходнаяТаблица.Номенклатура КАК Номенклатура,
ВТ_ИсходнаяТаблица.Количество КАК Количество,
ВТ_ИсходнаяТаблица.Сумма КАК Сумма
ИЗ
ВТ_РеквизитыШапки КАК ВТ_РеквизитыШапки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ИсходнаяТаблица КАК ВТ_ИсходнаяТаблица
ПО ВТ_РеквизитыШапки.Организация = ВТ_ИсходнаяТаблица.Организация
И ВТ_РеквизитыШапки.Контрагент = ВТ_ИсходнаяТаблица.Контрагент
И ВТ_РеквизитыШапки.Грузополучатель = ВТ_ИсходнаяТаблица.Грузополучатель
И ВТ_РеквизитыШапки.Договор = ВТ_ИсходнаяТаблица.Договор
ИТОГИ
МАКСИМУМ(Организация),
МАКСИМУМ(Контрагент),
МАКСИМУМ(Грузополучатель),
МАКСИМУМ(Договор),
СУММА(Сумма)
ПО
ИдентификаторНабора
Воспользовавшись функцией АВТОНОМЕРЗАПИСИ, мы идентифицировали все наборы Организация-Контрагент-Грузоотправитель-Договор и выполнили группировку в рамках целого набора, а не по каждому полю отдельно.
Код обработки такого запроса выглядит так же просто, как и в первоначальном случае с единственным группировочным полем:
ВыборкаПоНаборам = Запрос.Выполнить.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
Пока ВыборкаПоНаборам.Следующий() Цикл
// Заполнение шапки
ДетальныеЗаписи = ВыборкаПоНаборам.Выбрать();
Пока ДетальныеЗаписи.Следующий() Цикл
// Заполнение ТЧ товаров
КонецЦикла;
КонецЦикла;