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