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

20.11.14

Разработка - Запросы

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


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

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

пример отчета с рекурсией вот тут -  http://infostart.ru/projects/827/?ref=8784
в примере также дана обучающая обработка по работе с запросом, очень будет полезна тем кто только начинает знакомится с таким объектом как "запросом"
конфигурация с примером вот тут
//infostart.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;
КонецЦикла;

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

См. также

Запросы Программист Платформа 1С v7.7 Платформа 1С v8.3 Бесплатно (free)

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

09.03.2016    38827    Serginio    22    

44

Универсальные функции Запросы Программист Платформа 1С v7.7 Платформа 1С v8.3 Бесплатно (free)

На эту тему уже есть статьи, но этот способ нигде не описан. Хотя я его использую с тех пор, как занимаюсь программированием. Его преимущество в простоте и универсальности: можно применять на 1С, SQL, а также в любом другом языке программирования.

05.07.2015    22213    json    3    

22

Запросы Программист Платформа 1С v7.7 Конфигурации 1cv7 Абонемент ($m)

Пример получения остатков по складу по запросу по почте из программы 1С 7.7. Для получения остатков необходимо пользователю с любого почтовика (с любого "мыла") отправить текст сообщения GiveMyStockBalance_ForAnalize на почтовый адрес определенный в Константа.СерверПолучения. Программа выдаст остатки (можно переписать функцию для выдачи любых данных) в формате xls на почту указанную в константе Константа.СерверОтправки. Программа может быть полезна в тех организациях где трудно или невозможно осуществить прямой доступ к 1С сотрудников для просмотра необходимых данных. Также можно организовать некий почтамт - запрос для клиентов - при посылке определенного логина клиентом на его почту будет автоматически выслана информация, например, акт сверки с клиентов, или процент выполнения его заказа и т.д.

3 стартмани

25.03.2014    25466    5    protexprotex    3    

5

Зарплата Запросы Программист Расчет 7.7 1С:Зарплата и кадры 7.7 Абонемент ($m)

Обзор методов реализации условий и функций с условиями в обращениях к журналу расчетов при помощи прямого запроса.

1 стартмани

06.12.2012    14321    nicotin    5    

9

Запросы Платформа 1С v7.7 Оперативный учет 7.7 Бухгалтерский учет 7.7 Расчет 7.7 Конфигурации 1cv7 Абонемент ($m)

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

1 стартмани

14.04.2012    34325    309    set2333    16    

11
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. 14.03.07 11:53 Сейчас в теме
Может я чего не понимаю, но зачем в качестве статьи выкладывать чистый код с минимумом комментариев? Достаточно было в комментарии к обработке написать, что в ней реализуется пример того-то и того-то
2. brr 184 20.03.07 15:37 Сейчас в теме
Откройте ТиС, посмотрите, не изобретайте велосипед, а ведь еще есть RegPrint.ert!
3. GSoft 2237 21.03.07 23:36 Сейчас в теме
Да я и не старался ничего изобретать. Просто увидел статью на тему рекурсии и вспомнил про эту штуку,Выложил, может кому-нибудь понадобится.
4. logarifm 1122 24.04.07 17:51 Сейчас в теме
Интересное решение, рекурсия всегда была нелегкой и иттерация тоже ! +1 только за решение при этом в запросе.
5. GSoft 2237 24.04.07 23:59 Сейчас в теме
6. GSoft 2237 08.08.07 11:54 Сейчас в теме
2Brr

кстати, я тут вспомнил с чего это началось:))))
откройте типовую ПУБ, там есть отчет связанный с остатками, так вот там есть возможность выбора большого количества группировок, но МАКСИМАЛЬНОЕ возможное количество при построении отчета всегда ограничено 5 позициями, а надо было на тот момент 8. Ну а где 8, там и 10 :)))) и т.д.
7. poppy 10.08.07 01:37 Сейчас в теме
(6)
Ты ПУБ не трогай. В ней все коряво написано.

В ТиС отчеты реализованы с помощью рекурсии, но в ПУБ с помощью вложенных циклов. При этом, в ТиС ограничение в 5 группировок, но в ПУБ - 8. И связано это не в способе реализации алгоритма, но в том, что в макете отчета имеется ограниченное количество секций.
12. GSoft 2237 14.08.07 17:50 Сейчас в теме
но ведь никто и не говорил что описанное мной в п (6) реализуется вышеизложенным кодом

от простого к сложному не получается, сейчас итак пришлось курс ужать до 96 академчасов, а я все еще продолжаю читать материал из расчета 120 академчасов, несмотря на то что и моя программа выходила далеко за рамки этих 120 часов. На самом деле пример приурочен к изучению запроса, чтобы слушатели знали что в 1С можно использовать рекурсию и понимали общий принцип использования. Что касается запроса, то для сокращения времени пришлось написать маленькую обработку на базе учебной конфигурации, в которой собран как теоретический материал, и так и практическая реализация запроса.
8. GSoft 2237 13.08.07 12:57 Сейчас в теме
2poppy

а кто мешает начиная, скажем, с 5 уровня вложенности использовать одну и ту же секцию, или использую динамические секции делать сдвиг по горизонтали

з.ы. да никто собстно пуб и не трогает просто описал историю появления на свет сабжа:-)
9. poppy 13.08.07 13:12 Сейчас в теме
(8)
Ничто не мешает. Просто, в типовых конфигурациях это не реализовано. Проще ограничить количество одновременно используемых группировок.

З.Ы. В твоем коде это также не реализовано.
10. GSoft 2237 14.08.07 13:25 Сейчас в теме
2poppy

не будем разводить демагогию и меряться кодерами :)))))
в этом коде это действительно не реализовано по одной просто причине, это учебный пример который я даю своим слушателям на 4-5 занятии в рамках курса "Программирование и конфигурирование 7.7"
Мало того что для них в это время запрос является довольно сложной вещью, а тут еще рекурсия и динамические секции. Слишком много информации. Да и тем более это пример я им просто показываю, иногда даже особо не вдаваясь в глубину рекурсии, а отдавая это на откуп тем кому это интересно, и уже с ними потом отдельно все разбираю.

Что же касается полного варианта - то у я ограничился 7 группировками, а дальше делал сдвиги, почему цифра именно такая - просто напросто метод научного тыка в рамках поставленной на тот момент задачи - такая разбивка дала самое оптимальное отображение для восприятия
11. poppy 14.08.07 14:20 Сейчас в теме
(10) > не будем разводить демагогию и меряться кодерами :)))))

Никто ничем не меряется. Просто, решение, которое ты описал в своей статье неполностью решает задачу описанную в (6)

> Мало того что для них в это время запрос является довольно сложной вещью, а тут еще рекурсия и динамические секции. Слишком много информации.

Если информации много, может есть смысл разбить пример на несколько? Как говорится "от простого к сложному".
13. GSoft 2237 14.08.07 17:53 Сейчас в теме
.... в которой собран как теоретический материал, и так и практическая реализация запроса.
Если интересно могу выложить. Хотя данная обработка представляет интерес для начинающих программистов.
14. anig99 2852 09.10.09 08:58 Сейчас в теме
Чего-то меня настораживает уровень программистов 1с... Рекурсию и прочие СТАНДАРТНЫЕ приемы программирования мы проходили в школе...до 9 класса. Не вижу ничего сложного в рекурсии.
ИМХО что-то не то дают в институтах на курсах по программированию 1с. Сначала нужно стать программистом (ну хоть чуть-чуть, хотя думать как программист), а потом уже 1с...
З.Ы. Если уж пошла такая пляска может тогда выложить такие статьи:
1. Как поменять местами значение двух числовых переменных
2. Сортировка методом пузырька
3. Ускорения поиска значения делением массива
4. Разбиение чисел на десятки, сотни, тысячи и т.д.
5. Структурированый код - залог здоровья (как правильно использовать TAB)
Арчибальд; +1 Ответить
15. Арчибальд 2709 09.10.09 09:07 Сейчас в теме
(14) В одноэсники идут не только программисты, но и кто попало. Последних - больше. Для них твои пункты 2 и 3 - уже запредельщина, пункт 1 - вне сферы интересов и т.д. Автора, занимающегося обучением таких персонажей, можно только пожалеть :D
20. GSoft 2237 13.10.09 10:37 Сейчас в теме
(15) полностью согласен с Арчибальдом
не знаю как сейчас обстоит дело с 8-кой, но когда я преподавал программирование не 7-ке приходили люди которые и с операционной системой то плохо ладили))) не говоря уже о программировании, методах сортировки и т.д.))))

(19) если Вы внимательно посмотрите даты размещения на портале то увидите что это не вчера и даже не позавчера)))
а на тот момент не было возможности в статьях вставлять файлы а в программах описывать код, так что отсюда и две темы. Собстно у меня еще пара таких же ситуаций по выложенным мной материалам.
p.s. предложите свое название
21. anig99 2852 13.10.09 10:45 Сейчас в теме
(20) не...ну по факту-то я тоже согласен, что 1с программистами называют кого попало...но ведь это не основание для утверждения, что так должно быть. Я ратую за то, чтобы на всяческих курсах преподавали элементарные основы программирования: структурирование кода, простейшие алгоритмы сортировки, особенности применения процедур и функций (в том числе и рекурсию), булева алгебра...
16. Ёпрст 1065 09.10.09 09:19 Сейчас в теме
(0) А чего, типовые не кошерно глядеть?
Там всё это есть...
если что.
17. tango 545 09.10.09 09:27 Сейчас в теме
1.
a = a + b;
b = a - b;
a = a - b;
типа может кому интересно...
ну типа статья :))
18. artbear 1563 09.10.09 10:45 Сейчас в теме
Нормальная статья, что накинулись-то?
Как раз подобные статьи могут быть полезными для людей-непрограммистов, пришедших в 1С :)
19. anig99 2852 09.10.09 19:17 Сейчас в теме
(18) непрограммисты не должны программировать...
и статья не раскрывает суть рекурсии... на минус конечно не тянет, но называть статью ТАК не правильно, и уж тем более 2 раза выкладывать одно и тоже... Мда....
22. GSoft 2237 13.10.09 11:11 Сейчас в теме
так кто же с этим спорит
но как ни печально но это факт, у меня к примеру сейчас в политехе(колледж) на третьем курсе студенты не знают что такое естественный алгоритмический язык, вот и говори после этого с ними о рекурсии, не говоря уже о системном моделировании которое я им читаю и которое должно базироваться на очень большом количестве фундаментальных вещей
23. пользователь 02.12.11 13:31
Сообщение было скрыто модератором.
...
Оставьте свое сообщение