Три способа получить дерево элементов иерархического справочника
Разработка - Математика и алгоритмы
1) Запрос с использованием итогов по иерархии
2) Формирование дерева обходом выборки с упорядочиванием по иерархии
3) Формирование иерархии по списку элементов транзитивным замыканием
1) Идея проста - выбираем запросом элементы, не являющиеся папками, а всю иерархию нам построит запрос. Тут сразу начинаются неожиданности. Какую конструкцию использовать: ИЕРАРХИЯ или ТОЛЬКО ИЕРАРХИЯ? Вроде логично было бы ТОЛЬКО ИЕРАРХИЯ, т.к. итоги на уровне элементов нам не нужны (будут дубли). Заглядываем в справку: "ИЕРАРХИЯ. В результате будут рассчитаны итоги по контрольным точкам и итоги по иерархии для контрольных точек ... При необходимости можно рассчитать итоги только значений по иерархии, без расчета итогов в контрольных точках. Для этого перед ключевым словом ИЕРАРХИЯ нужно указать ключевое слово ТОЛЬКО."
Для однозначного понимания моих объяснений введу несколько "терминов", которыми буду пользоваться. Все листья дерева буду называть элементами. Узлы дерева, которые содержат только элементы - нижние папки, Остальные узлы, которые содержат хотя бы одну нижнюю папку - верхние папки.
Для ИЕРАРХИЯ - все логично: разбираем дерево итогов по иерархии для папок. У всех папок тип - ТипЗаписиЗапроса.ИтогПоИерархии. У элементов тип - ТипЗаписиЗапроса.ИтогПоГруппировке. Внутри группировки одна запись того же элемента но уже с типом ТипЗаписиЗапроса.ДетальнаяЗапись. Все как заявлено. Но если выгрузить в дерево, дубль пропадает!
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
// Тут могут быть условия
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура.ЭтоГруппа,
| Ссылка
|ИТОГИ ПО
| Ссылка ИЕРАРХИЯ
|АВТОУПОРЯДОЧИВАНИЕ";
Дерево=Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Либо сформировать вручную
ВыборкаСИерархией=Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка");
Дерево=Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Номенклатура");
ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево);
КонецПроцедуры
Процедура ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево)
Пока ВыборкаСИерархией.Следующий() Цикл
Если ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоИерархии Тогда
Строка=Дерево.Строки.Добавить();
Строка.Номенклатура=ВыборкаСИерархией.Ссылка;
ВыбратьЭлементыВИерархии (ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка"),Строка);
ИначеЕсли ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоГруппировке Тогда
//Тут на самом деле есть еще один уровень, но нам он может потребоваться,
//только если нужно взять данные на уровне записи
//Если это нужно, здесь обходим один элемент, забираем из него данные
//Выборка=ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам
//Пока ВыборкаСледующий() Цикл
// Строка=Дерево.Строки.Добавить();
// Строка.Номенклатура=ВыборкаСИерархией.Ссылка;
//КонецЦикла;
Строка=Дерево.Строки.Добавить();
Строка.Номенклатура=ВыборкаСИерархией.Ссылка;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Для ТОЛЬКО ИЕРАРХИЯ все немного не так, как ожидалось. Верхние папки - ТипЗаписиЗапроса.ИтогПоИерархии, нижние - ТипЗаписиЗапроса.ИтогПоГруппировке. Внутри элементы с типом - ТипЗаписиЗапроса.ДетальнаяЗапись. НО, если верхняя папка содержит элементы, все они будут помещены в еще в одну вложенную группу с типом ТипЗаписиЗапроса.ИтогПоГруппировке. Поэтому выгрузить() приводит к дублированию! Цель такого дублирования думаю в том, чтобы все элементы обязательно содержались в папке с ТипЗаписиЗапроса.ИтогПоГруппировке, чтобы мы могли обходить выборку ОбходРезультатаЗапроса.ПоГруппировкам. Поэтому, если использовать ТОЛЬКО ИЕРАРХИЯ, лучше обойти выборку и сформировать ручками дерево, устраняя дублирование. При обходе нужно обязательно указывать второй параметр "Группировки". Привожу обход для ТОЛЬКО ИЕРАРХИЯ
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| НЕ Номенклатура.ЭтоГруппа
// Тут могут быть условия
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура.ЭтоГруппа,
| Ссылка
|ИТОГИ ПО
| Ссылка ТОЛЬКО ИЕРАРХИЯ
|АВТОУПОРЯДОЧИВАНИЕ";
Результат=Запрос.Выполнить();
ВыборкаСИерархией=Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка");
Дерево=Новый ДеревоЗначений;
Дерево.Колонки.Добавить("Номенклатура");
ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево);
Процедура ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево)
Пока ВыборкаСИерархией.Следующий() Цикл
Если ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоИерархии Тогда
Строка=Дерево.Строки.Добавить();
Строка.Номенклатура=ВыборкаСИерархией.Ссылка;
ВыбратьЭлементыВИерархии (ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка"),Строка);
ИначеЕсли ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоГруппировке Тогда
Если Не ЗначениеЗаполнено(ВыборкаСИерархией.Ссылка) ИЛИ ТипЗнч(Дерево)=Тип("СтрокаДереваЗначений") И ВыборкаСИерархией.Ссылка=Дерево.Номенклатура Тогда
ВыбратьЭлементыБезИерархии(ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам),Дерево);
Иначе
Строка=Дерево.Строки.Добавить();
Строка.Номенклатура=ВыборкаСИерархией.Ссылка;
ВыбратьЭлементыБезИерархии(ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам),Строка);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ВыбратьЭлементыБезИерархии (ВыборкаБезИерархии,Дерево)
Пока ВыборкаБезИерархии.Следующий() Цикл
Строка=Дерево.Строки.Добавить();
Строка.Номенклатура=ВыборкаБезИерархии.Ссылка;
КонецЦикла;
КонецПроцедуры
Недостатки такого метода очевидны. Все папки вычисляет запрос, и мы не можем как-то их использовать. Например, при соединении с другой таблицей по номенклатуре, склеются только элементы, а на уровне папок нам доступны только вычисления в итогах. Проблема производительности здесь не рассматривается.
2) Для решения этой проблемы необходимо выбрать папки в запросе. Такой запрос не получиться выгрузить в дерево, но, если мы будем использовать в запросе УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ, а также выберем в запросе родителя, то обход дерева станет простым, мы будем обходить выборку в цикле и прицеплять следующий элемент к текущему или одному из его родителей. К кому цеплять покажет выбранное поле родитель.
Запрос = Новый Запрос;
Запрос.Текст ="
|ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Родитель КАК Родитель
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
// Тут могут быть условия
|
|УПОРЯДОЧИТЬ ПО
| Номенклатура.Ссылка ИЕРАРХИЯ
|АВТОУПОРЯДОЧИВАНИЕ";
Результат = Запрос.Выполнить();
ДеревоПапок=Новый ДеревоЗначений;
ДеревоПапок.Колонки.Добавить("Номенклатура");
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Если Не ЗначениеЗаполнено(Выборка.Родитель) Тогда
ТекЭлемент=ДеревоПапок.Строки.Добавить();
ТекЭлемент.Номенклатура=Выборка.Ссылка;
Иначе
Пока ТекЭлемент.Номенклатура<>Выборка.Родитель Цикл
ТекЭлемент=ТекЭлемент.Родитель;
КонецЦикла;
ТекЭлемент=ТекЭлемент.Строки.Добавить();
ТекЭлемент.Номенклатура=Выборка.Ссылка;
КонецЕсли;
КонецЦикла;
Этот метод хорош, но его невозможно применить, если мы не можем получить сразу всю иерархию. Если у нас есть только набор элементов, нам необходимо предварительно построить всю иерархию.
3) Итак, у нас есть набор элементов (набор условий отбора на элементы), но мы не знаем их родителей, нам нужно их вычислить, а затем сформировать дерево. Тут нам не обойтись без Сергея (ildarovich) (отдельное спасибо ему за качественный контент) и его публикации //firstportal.ru/public/158512/
Рассмотрим задачу получения только иерархии по набору элементов. Для решения задачи выберем для элементов все папки, в которых они содержатся, затем замыканием вычислим всех родителей этих папок, ну и далее выборка с обходом по 2 методу.
Запрос = Новый Запрос;
Запрос.Текст =ТранзитивноеЗамыкание(256);
Результат = Запрос.Выполнить();
ДеревоПапок=Новый ДеревоЗначений;
ДеревоПапок.Колонки.Добавить("Номенклатура");
Выборка = Результат.Выбрать();
Пока Выборка.Следующий() Цикл
Если Не ЗначениеЗаполнено(Выборка.Родитель) Тогда
ТекЭлемент=ДеревоПапок.Строки.Добавить();
ТекЭлемент.Номенклатура=Выборка.Ссылка;
Иначе
Пока ТекЭлемент.Номенклатура<>Выборка.Родитель Цикл
ТекЭлемент=ТекЭлемент.Родитель;
КонецЦикла;
ТекЭлемент=ТекЭлемент.Строки.Добавить();
ТекЭлемент.Номенклатура=Выборка.Ссылка;
КонецЕсли;
КонецЦикла;
Функция ТранзитивноеЗамыкание(МаксимальнаяДлинаПути)
//Эмуляция отбора элементов. Выборка Папок, в которых они содержаться, затем замыкаем
Пролог = "ВЫБРАТЬ Ссылка ПОМЕСТИТЬ ВТ_Элементы ИЗ Справочник.Номенклатура КАК Номенклатура ГДЕ НЕ ЭтоГруппа;
|ВЫБРАТЬ РАЗЛИЧНЫЕ Ссылка.Родитель КАК Ссылка, Ссылка.Родитель.Родитель КАК Родитель ПОМЕСТИТЬ ВТ_Папки ИЗ ВТ_Элементы КАК ВТ_Элементы;
|ВЫБРАТЬ Родитель НачалоДуги, Ссылка КонецДуги ПОМЕСТИТЬ ЗамыканияДлины1 ИЗ ВТ_Папки
| ГДЕ Родитель <> Значение(Справочник.Номенклатура.ПустаяСсылка)
| ОБЪЕДИНИТЬ ВЫБРАТЬ Ссылка, Ссылка ИЗ ВТ_Папки;";
Рефрен = "ВЫБРАТЬ РАЗЛИЧНЫЕ ПерваяДуга.НачалоДуги, ВтораяДуга.КонецДуги ПОМЕСТИТЬ ЗамыканияДлины#2 ИЗ ЗамыканияДлины#1 КАК ПерваяДуга
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЗамыканияДлины#1 КАК ВтораяДуга ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги;
| УНИЧТОЖИТЬ ЗамыканияДлины#1;";
Эпилог = "ВЫБРАТЬ РАЗЛИЧНЫЕ НачалоДуги КАК Ссылка ПОМЕСТИТЬ ВТ_ВсеПапки ИЗ ЗамыканияДлины#2 ГДЕ НачалоДуги<>Значение(Справочник.Номенклатура.ПустаяСсылка);
|ВЫБРАТЬ Ссылка, Ссылка.Родитель КАК Родитель ИЗ ВТ_ВсеПапки УПОРЯДОЧИТЬ ПО Ссылка ИЕРАРХИЯ АВТОУПОРЯДОЧИВАНИЕ";
ТекстЗапроса = Пролог;
МаксимальнаяДлинаЗамыканий = 1;
Пока МаксимальнаяДлинаЗамыканий < МаксимальнаяДлинаПути Цикл
ТекстЗапроса = ТекстЗапроса + СтрЗаменить(СтрЗаменить(Рефрен, "#1", Формат(МаксимальнаяДлинаЗамыканий, "ЧГ=0")), "#2", Формат(2 * МаксимальнаяДлинаЗамыканий, "ЧГ=0"));
МаксимальнаяДлинаЗамыканий = 2 * МаксимальнаяДлинаЗамыканий
КонецЦикла;
ТекстЗапроса = ТекстЗапроса + СтрЗаменить(Эпилог, "#2", Формат(МаксимальнаяДлинаЗамыканий, "ЧГ=0"));
Возврат ТекстЗапроса;
КонецФункции
Специальные предложения
См. также
Регистры бухгалтерии. Общая информация 111
05.09.2019 6697 YPermitin 22
"Хочу универсально!" [Часть 1] 65
02.09.2019 4900 SeiOkami 35
Иерархия без "В ИЕРАРХИИ" 117
22.08.2019 4913 ildarovich 16
EnterpriseData – часть 3. Загрузка данных, идентификация объектов 62
22.08.2019 4230 ids79 7
Обработчики событий при записи объектов. Зачем и что за чем? 202
25.07.2019 12801 4 AlbinaAAA 23
Как проводятся документы в типовых конфигурациях от 1С 137
24.07.2019 16075 skv_79 32
FizzBuzz на 1С. Чем короче, тем веселее. Варианты принимаются... 8
24.07.2019 2863 vandalsvq 16
Управление качеством кода 136
22.07.2019 8233 Stepa86 29
Что делает "В ИЕРАРХИИ" в запросе? 94
16.07.2019 8182 YPermitin 34
Создание отчетов с помощью СКД - основные понятия и элементы 208
25.06.2019 20977 ids79 17
Реализуем Стек, Очередь и Приоритетную очередь в 1С 52
24.06.2019 7804 RonX01 63
Организация хранения промежуточных данных 3
29.05.2019 1953 scientes 1
Вычисление 200 тысяч знаков числа pi 73
28.05.2019 3983 Oleg_nsk 93
Регистры накопления. Виртуальные таблицы. Часть №1: Обороты 84
20.05.2019 11069 YPermitin 5
Даем названия переменным: как префиксы экономят наше время 10
06.05.2019 3213 Designer1C 69
Заметки по SQL: Срез последних - аналог запроса 15
15.01.2019 6317 IVC_goal 5
Разработка и сценарное тестирование с Vanessa-ADD. Концепция, теория и сквозной пример создания сценария 222
09.01.2019 27568 Vladimir Litvinenko 69
Многопоточное восстановление последовательностей 41
05.12.2018 7248 _ASZ_ 29
Автоматические и управляемые блокировки применительно к типовым конфигурациям 1С 127
10.11.2018 22244 ids79 40
Основные понятия и механизмы оптимизации клиент-серверного взаимодействия в 1C 147
23.08.2018 22869 Rain88 42
Теорема номер тринадцать 15
15.03.2018 9358 vasilev2015 24
Введение в CI для 1С 87
21.11.2017 19331 real_MaxA 22
Как работает серверный вызов в 1С 459
18.11.2017 44243 pahich 77
#Область ВНЕШНИЕ_ВЫЗОВЫ или MVC в 1С, библиотечность и упрощение интеграции кода 43
12.10.2017 14809 for_sale 58
Групповая разработка конфигураций в крупном холдинге 68
15.08.2017 17545 stas_ganiev 15
Автоматизация процесса 1С-разработки 91
07.06.2017 23028 ekaruk 9
Пишем игру Минер. Обработка событий ActiveX в 1С 29
29.05.2017 12732 user621724_Dimav1979 11
Как я доступ на kb.1c.ru получал 91
01.05.2017 22534 ikekoval 33
Улучшение стандарта "Структура модуля" 6
26.03.2017 12324 o.nikolaev 23
"Распределение в запросе" или "избавляемся от перебора" 185
16.12.2016 28631 alexandersh 48
Планы обмена. Квитировать или гарантировать? 24
12.12.2016 14558 zhichkin 9
Некоторые принципы оптимизации запросов 1С (+SQL) 115
17.11.2016 8898 ture 40
Использование git для доработки типовых конфигураций 1С 230
11.10.2016 188456 pumbaE 31
Оптимизация запросов 1С:Предприятие – от теории к практике 116
07.10.2016 32080 bpc222 20
Регистры сведений 1С. Как это устроено. 729
05.08.2016 151048 Sergey.Noskov 155
Переводим расширения на 8.3.8. Памятка. 79
29.07.2016 39710 mrXoxot 12
Подобие Объектно-ориентированного программирования в 1С (ПООПс) 12
24.07.2016 10916 adam26 54
Опыт практического применения методики BDD на 1С. Написание сценариев 121
03.07.2016 20335 oleynik.dv 132
Заметки про запросы. Последовательность. 110
27.05.2016 29630 vasilev2015 31
Оптимизация планирования доставки грузов. Алгоритм кластеризации k-means (метод K-средних). 26
10 стартмани
09.02.2016 26526 mi1man 4
Контур.EDI изнутри, или история командной разработки тиражного продукта на 1С 174
17.11.2015 36044 skif47 88
Порядок записи движений регистров при проведении документа 95
13.11.2015 80526 triton_tver 8
Распределение суммы по базе 48
08.11.2015 27731 starik-2005 19
Мультиинструментальный Brute Force 4
30.10.2015 10442 scientes 4
1С с "плюсами" 74
14.10.2015 19970 IntelInside 47
Знакомство с технологией Automation-сервер на примерах 33
28.09.2015 26192 niko11s 10
Критерии отбора 83
24.09.2015 49477 niko11s 13
По ссылке или по значению? Ключевое слово Знач и с чем его едят 196
12.08.2015 37186 Evil Beaver 239
Приемы обработки больших данных в 1С 258
07.08.2015 60324 tormozit 27