Каталог решений - Обучение: Использование рекурсии при обращении к результатам выполнения запроса

Обучение: Использование рекурсии при обращении к результатам выполнения запроса

Обучение: Использование рекурсии при обращении к результатам выполнения запроса

В наличии

Коллеги! Не пожалейте несколько секунд нажав на плюсик, вам мелочь, а мне приятно)))))
Также оставляйте замечания, комментарии и пожелания.

В продолжение темы от O-Planet  
Мастер класс «O-Planet»: использование рекурсивных вычислений в 1С — http://infostart.ru/articles/82/?ref=8784

Пару лет назад столкнулся с тем, что в ПУБе в некоторых отчетах количество группировок ограничено 5 уровнями и это при списке из позиций кажется 12.  Честно говоря, как-то не вдохновило описывать 12 реальных циклов, решил написать рекурсию. Потом функция переросла в практический пример, который я привожу своим слушателям для демонстрации того, как реализовывать рекурсию при использовании запроса.

пример отчета с рекурсией вот тут —  http://infostart.ru/projects/827/?ref=8784
в примере также дана обучающая обработка по работе с запросом, очень будет полезна тем кто только начинает знакомится с таким объектом как "запросом"

Категория:

Описание

конфигурация с примером вот тут
//sale.itcity.ru/projects/827/

В данном примере строится отчет на базе трех группировок которые определяются при открытии формы, таким для использования рекурсии в других целях достаточно изменить запрос и эту процедуру. Хотя при наличии времени возможно описать универсальную процедуру в которой можно предоставить пользователю на первом шаге выбрать из метаданных интересующие его объекты, а на втором — атрибуты этих объектов, которые и будут являться группировками запроса. Далее на основании этих данных автоматически формировать ТекстЗапроса.

 

Перем КолвоГруппировок;

//___________________________________________________*
Процедура Группировка(ИмяЗапроса, ОбщКолвоГруппировок, ТекущаяГруппировка, СписЗначГрупп, ИмяТаблицы) Далее



//___________________________________________________*
Процедура ПриОткрытии()
СЗРасш.ДобавитьЗначение("Документ", "Документ");
СЗРасш.ДобавитьЗначение("День", "День");
СЗРасш.ДобавитьЗначение("Неделя", "Неделя");
СЗРасш.ДобавитьЗначение("Месяц", "Месяц");
СЗРасш.ДобавитьЗначение("Квартал", "Квартал");
СЗРасш.ДобавитьЗначение("Год", "Год");

СЗГрупп.ДобавитьЗначение("Сотрудник", "Сотрудник");
СЗГрупп.ДобавитьЗначение("Блюдо", "Блюдо");
СЗГрупп.ДобавитьЗначение("Интервал", "Интервал");

Форма.СЗРасш.Видимость(0);
Форма.ИмяСЗРасш.Видимость(0);

КолвоГруппировок = СЗГрупп.РазмерСписка();
КонецПроцедуры

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

//___________________________________________________*
Процедура Сформировать()
Перем Запрос
, ТекстЗапроса, Таб;

ГруппировкаВыбрана = 0;
//определение выборки хотя бы одной группировки
Для Сч = 1 По КолвоГруппировок Цикл
Если
СЗГрупп.Пометка(Сч) = 1 Тогда
ГруппировкаВыбрана = 1;
КонецЕсли;
КонецЦикла;

Если ГруппировкаВыбрана = 0 Тогда
Предупреждение("Вы не указали ни одной группировки", 60);
Возврат;
КонецЕсли;

//определение указания расшифровки по интервалу
Если (СЗГрупп.Пометка(СЗГрупп.НайтиЗначение("Интервал"),) = 1) И (Форма.СЗРасш.Видимость() = 0) Тогда
Предупреждение("Вы не выбрали периодичность интервала", 60);
ВидимостьРасшифровки();
Возврат;
КонецЕсли;


//Создание объекта типа Запрос
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с ВыбНачПериода по ВыбКонПериода;
|ОбрабатыватьДокументы Проведенные;
|Обрабатывать НеПомеченныеНаУдаление;
|Сотрудник = Документ.ЗаказСотрудника.Сотрудник;
|Блюдо = Документ.ЗаказСотрудника.Блюдо;
|Количество = Документ.ЗаказСотрудника.Количество;
|Стоимость = Документ.ЗаказСотрудника.Сумма;
|Функция КолСумма = Сумма(Количество);
|Функция СтоимСумма = Сумма(Стоимость);
|"//}}ЗАПРОС
;

//доп СЗ для вызова процедуры-рекурсии группировка
//у данного списка значений - две функции:
//во-первых, его размер будет говорить о том сколько раз будет запущена рекурсия
//Во-вторых, значения, помещенные в него - идентификаторы группировок к которым мы будем //обращаться в рекурсии
СЗГруппРек = СоздатьОбъект("СписокЗначений");

Для Сч = 1 По КолвоГруппировок Цикл
Если
СЗГрупп.Пометка(Сч) = 1 Тогда
Если
СЗГрупп.ПолучитьЗначение(Сч,) = "Интервал" Тогда
Интервал = СЗРасш.ПолучитьЗначение(СЗРасш.ТекущаяСтрока());
ТекстЗапроса = ТекстЗапроса + "Группировка " + Интервал + ";";

СЗГруппРек.ДобавитьЗначение(Интервал, Интервал);

Иначе
ЗначениеГруппировки = СЗГрупп.ПолучитьЗначение(Сч);
ТекстЗапроса = ТекстЗапроса + "Группировка " + ЗначениеГруппировки + ";";
СЗГруппРек.ДобавитьЗначение(ЗначениеГруппировки, ЗначениеГруппировки);
КонецЕсли;
КонецЕсли;
КонецЦикла;

// Если ошибка в запросе, то выход из процедуры
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат
;
КонецЕсли;

СписокОбъектов = "";

//Формируем заголовок колонок табличной части
Для Сч = 1 По КолвоГруппировок Цикл
Если
СЗГрупп.Пометка(Сч) = 1 Тогда

СЗГрупп.ПолучитьЗначение(Сч,СписокОбъектов);
Если Сч =1 Тогда
Заголовок = СписокОбъектов;
Иначе
Заголовок = Заголовок + "/" + СписокОбъектов;
КонецЕсли;
КонецЕсли;
КонецЦикла;

// Подготовка к заполнению выходных форм данными запроса
Таб = СоздатьОбъект("Таблица");
Таб.ИсходнаяТаблица("Сформировать");
// Заполнение полей "Заголовок"
Таб.ВывестиСекцию("Заголовок");
Состояние("Заполнение выходной таблицы...");
Таб.Опции(0, 0, Таб.ВысотаТаблицы(), 0);


//определяем количество циклов
ОбщКолвоРезГруппировок = СЗГруппРек.РазмерСписка();

//В этом месте по обычаю находились бы вложенные циклы с обходом по группировкам
//результатов выполнения запроса.
//Мы же здесь вызываем рекурсию. В процедуру передаем ссылку на сам запрос, для обращения
//к его результатам, общее количество группировок, номер первой группировки - так как
//здесь мы запускаем внешний цикл и таблицу в которую формируем отчет
Группировка(Запрос, ОбщКолвоРезГруппировок, 1, СЗГруппРек, Таб);


// Заполнение полей "Итого"
Таб.ВывестиСекцию("Итого");
// Вывод заполненной формы
Таб.ТолькоПросмотр(1);
Таб.Показать("Сформировать", "");

КонецПроцедуры

И вот собственно сама рекурсия. Процедура вызывается всегда с 4-мя параметрами.
1. Ссылка на запрос к которму мы обращаемся
2. Общее количество группировок.
3. Один из двух основных параметров — номер группировки к которой будет идти обращение
4. Второй основной параметр — список значений в котором содержатся в иерархическом порядке идентификаторы группировок к которым происходит обращение в данном вызове процедуры. Группировка определяется исходя из значения предыдущего параметра
5. И собственно говоря таблица в которую выводим значения

В принципе параметры 1,2 и 5 можно сделать общими для всего модуля, тогда их можно будет убрать из параметров процедуры

Общая идея процедуры такова — при входе в процедуру получаем идентификатор текущей группировки и запускаем по ней цикл. Здесь же обращаемся к атрибутам группировки и заполняем соответствующие переменные и выводим секции в таблицу.
Далее увеличиваем номер текущей группировки — то есть готовимся к обходу результатов по следующей (вложенной) группировке. Сравниваем полученный номер с общим количеством группировок и если он меньше то запускаем рекурсивную процедуру с теми же параметрами но уже с номером вложенной группировки и т.д. В самой вложенной группировке переменная ТекущаяГруппировка будет на единицу больше общего количества группировок, а посему рекурсию мы больше не запускаем. Уменьшаем номер группировки до текущего и переходим на следующий виток цикла. Как только мы доберемся до конца цикла мы просто выйдем из данной (самой вложенной) процедуры и вернемся в вышестоящую, там уменьшаем номер группировки до значения текущей на этом уровне группировки, и переходим на следующий виток цикла текущей группировки, где опять вызовем самую вложенную группировку и .д.
В конечном итоге после выполнения всех вложенных проццедур мы вернемся в процедуру Сформировать() где покажем заполненную в рекурсии таблицу, и закончим работу модуля.

//___________________________________________________*
//сама рекурсия
Процедура Группировка(ИмяЗапроса, ОбщКолвоГруппировок, ТекущаяГруппировка, СписЗначГрупп, ИмяТаблицы)
Пока
ИмяЗапроса.Группировка(ТекущаяГруппировка) = 1 Цикл

// Заполнение полей группировки
АтрибутГруппировки = "";
СписЗначГрупп.ПолучитьЗначение(ТекущаяГруппировка, АтрибутГруппировки);

Объект = ИмяЗапроса.ПолучитьАтрибут(АтрибутГруппировки);
СуммаПоОбъекту = ИмяЗапроса.КолСумма;
СтоимПоОбъекту = ИмяЗапроса.СтоимСумма;

Попытка
Если
Объект.ЭтоГруппа() = 1 Тогда
ИмяСекцииДляВывода = "Группа";
Иначе
ИмяСекцииДляВывода = "Группировка" + ТекущаяГруппировка;
КонецЕсли;
ИмяТаблицы.ВывестиСекцию(ИмяСекцииДляВывода);
Исключение
ИмяТаблицы.ВывестиСекцию("Группировка" + ТекущаяГруппировка);
КонецПопытки;

//увеличиваем номер группировки для входа во внутренний цикл
ТекущаяГруппировка = ТекущаяГруппировка + 1;

Если ТекущаяГруппировка <= ОбщКолвоГруппировок Тогда
Группировка(ИмяЗапроса, ОбщКолвоГруппировок, ТекущаяГруппировка, СписЗначГрупп, ИмяТаблицы);
КонецЕсли;

//уменьшаем номер группировки для входа во внешний цикл
ТекущаяГруппировка = ТекущаяГруппировка - 1;
КонецЦикла;

КонецПроцедуры

has been added to your cart:
Оформление заказа