Дерево справочников запросом 1с

Три способа получить дерево элементов иерархического справочника

Рассматривается применимость и недостатки следующих способов получения дерева
1) Запрос с использованием итогов по иерархии
2) Формирование дерева обходом выборки с упорядочиванием по иерархии
3) Формирование иерархии по списку элементов транзитивным замыканием

1) Идея проста — выбираем запросом элементы, не являющиеся папками, а всю иерархию нам построит запрос. Тут сразу начинаются неожиданности. Какую конструкцию использовать: ИЕРАРХИЯ или ТОЛЬКО ИЕРАРХИЯ? Вроде логично было бы ТОЛЬКО ИЕРАРХИЯ, т.к. итоги на уровне элементов нам не нужны (будут дубли). Заглядываем в справку: «ИЕРАРХИЯ. В результате будут рассчитаны итоги по контрольным точкам и итоги по иерархии для контрольных точек . При необходимости можно рассчитать итоги только значений по иерархии, без расчета итогов в контрольных точках. Для этого перед ключевым словом ИЕРАРХИЯ нужно указать ключевое слово ТОЛЬКО.» Для однозначного понимания моих объяснений введу несколько «терминов», которыми буду пользоваться. Все листья дерева буду называть элементами. Узлы дерева, которые содержат только элементы — нижние папки, Остальные узлы, которые содержат хотя бы одну нижнюю папку — верхние папки. Для ИЕРАРХИЯ — все логично: разбираем дерево итогов по иерархии для папок. У всех папок тип — ТипЗаписиЗапроса.ИтогПоИерархии. У элементов тип — ТипЗаписиЗапроса.ИтогПоГруппировке. Внутри группировки одна запись того же элемента но уже с типом ТипЗаписиЗапроса.ДетальнаяЗапись. Все как заявлено. Но если выгрузить в дерево, дубль пропадает!

Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | Номенклатура.Ссылка КАК Ссылка |ИЗ | Справочник.Номенклатура КАК Номенклатура |ГДЕ | НЕ Номенклатура.ЭтоГруппа // Тут могут быть условия
| |УПОРЯДОЧИТЬ ПО | Номенклатура.ЭтоГруппа, | Ссылка |ИТОГИ ПО | Ссылка ИЕРАРХИЯ |АВТОУПОРЯДОЧИВАНИЕ"; Дерево=Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
ВыборкаСИерархией=Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка"); Дерево=Новый ДеревоЗначений; Дерево.Колонки.Добавить("Номенклатура"); ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево); КонецПроцедуры Процедура ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево) Пока ВыборкаСИерархией.Следующий() Цикл Если ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоИерархии Тогда Строка=Дерево.Строки.Добавить(); Строка.Номенклатура=ВыборкаСИерархией.Ссылка; ВыбратьЭлементыВИерархии (ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка"),Строка); ИначеЕсли ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоГруппировке Тогда //Тут на самом деле есть еще один уровень, но нам он может потребоваться, //только если нужно взять данные на уровне записи //Если это нужно, здесь обходим один элемент, забираем из него данные //Выборка=ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам //Пока ВыборкаСледующий() Цикл // Строка=Дерево.Строки.Добавить(); // Строка.Номенклатура=ВыборкаСИерархией.Ссылка; //КонецЦикла; Строка=Дерево.Строки.Добавить(); Строка.Номенклатура=ВыборкаСИерархией.Ссылка; КонецЕсли; КонецЦикла; КонецПроцедуры

Для ТОЛЬКО ИЕРАРХИЯ все немного не так, как ожидалось. Верхние папки — ТипЗаписиЗапроса.ИтогПоИерархии, нижние — ТипЗаписиЗапроса.ИтогПоГруппировке. Внутри элементы с типом — ТипЗаписиЗапроса.ДетальнаяЗапись. НО, если верхняя папка содержит элементы, все они будут помещены в еще в одну вложенную группу с типом ТипЗаписиЗапроса.ИтогПоГруппировке. Поэтому выгрузить() приводит к дублированию! Цель такого дублирования думаю в том, чтобы все элементы обязательно содержались в папке с ТипЗаписиЗапроса.ИтогПоГруппировке, чтобы мы могли обходить выборку ОбходРезультатаЗапроса.ПоГруппировкам. Поэтому, если использовать ТОЛЬКО ИЕРАРХИЯ, лучше обойти выборку и сформировать ручками дерево, устраняя дублирование. При обходе нужно обязательно указывать второй параметр «Группировки». Привожу обход для ТОЛЬКО ИЕРАРХИЯ

Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ | Номенклатура.Ссылка КАК Ссылка |ИЗ | Справочник.Номенклатура КАК Номенклатура |ГДЕ | НЕ Номенклатура.ЭтоГруппа // Тут могут быть условия
| |УПОРЯДОЧИТЬ ПО | Номенклатура.ЭтоГруппа, | Ссылка |ИТОГИ ПО | Ссылка ТОЛЬКО ИЕРАРХИЯ |АВТОУПОРЯДОЧИВАНИЕ"; Результат=Запрос.Выполнить(); ВыборкаСИерархией=Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка"); Дерево=Новый ДеревоЗначений; Дерево.Колонки.Добавить("Номенклатура"); ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево); Процедура ВыбратьЭлементыВИерархии(ВыборкаСИерархией,Дерево) Пока ВыборкаСИерархией.Следующий() Цикл Если ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоИерархии Тогда Строка=Дерево.Строки.Добавить(); Строка.Номенклатура=ВыборкаСИерархией.Ссылка; ВыбратьЭлементыВИерархии (ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией,"Ссылка"),Строка); ИначеЕсли ВыборкаСИерархией.ТипЗаписи()=ТипЗаписиЗапроса.ИтогПоГруппировке Тогда Если Не ЗначениеЗаполнено(ВыборкаСИерархией.Ссылка) ИЛИ ТипЗнч(Дерево)=Тип("СтрокаДереваЗначений") И ВыборкаСИерархией.Ссылка=Дерево.Номенклатура Тогда ВыбратьЭлементыБезИерархии(ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам),Дерево); Иначе Строка=Дерево.Строки.Добавить(); Строка.Номенклатура=ВыборкаСИерархией.Ссылка; ВыбратьЭлементыБезИерархии(ВыборкаСИерархией.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам),Строка); КонецЕсли; КонецЕсли; КонецЦикла; КонецПроцедуры Процедура ВыбратьЭлементыБезИерархии (ВыборкаБезИерархии,Дерево) Пока ВыборкаБезИерархии.Следующий() Цикл Строка=Дерево.Строки.Добавить(); Строка.Номенклатура=ВыборкаБезИерархии.Ссылка; КонецЦикла; КонецПроцедуры

Недостатки такого метода очевидны. Все папки вычисляет запрос, и мы не можем как-то их использовать. Например, при соединении с другой таблицей по номенклатуре, склеются только элементы, а на уровне папок нам доступны только вычисления в итогах. Проблема производительности здесь не рассматривается. 2) Для решения этой проблемы необходимо выбрать папки в запросе. Такой запрос не получиться выгрузить в дерево, но, если мы будем использовать в запросе УПОРЯДОЧИТЬ ПО . ИЕРАРХИЯ, а также выберем в запросе родителя, то обход дерева станет простым, мы будем обходить выборку в цикле и прицеплять следующий элемент к текущему или одному из его родителей. К кому цеплять покажет выбранное поле родитель.

Запрос = Новый Запрос; Запрос.Текст ; Результат = Запрос.Выполнить(); ДеревоПапок=Новый ДеревоЗначений; ДеревоПапок.Колонки.Добавить("Номенклатура"); Выборка = Результат.Выбрать(); Пока Выборка.Следующий() Цикл Если Не ЗначениеЗаполнено(Выборка.Родитель) Тогда ТекЭлемент=ДеревоПапок.Строки.Добавить(); ТекЭлемент.Номенклатура=Выборка.Ссылка; Иначе Пока ТекЭлемент.Номенклатура<>Выборка.Родитель Цикл ТекЭлемент=ТекЭлемент.Родитель; КонецЦикла; ТекЭлемент=ТекЭлемент.Строки.Добавить(); ТекЭлемент.Номенклатура=Выборка.Ссылка; КонецЕсли; КонецЦикла;

Этот метод хорош, но его невозможно применить, если мы не можем получить сразу всю иерархию. Если у нас есть только набор элементов, нам необходимо предварительно построить всю иерархию.
3) Итак, у нас есть набор элементов (набор условий отбора на элементы), но мы не знаем их родителей, нам нужно их вычислить, а затем сформировать дерево. Тут нам не обойтись без Сергея (ildarovich) (отдельное спасибо ему за качественный контент) и его публикации //infostart.ru/public/158512/ Рассмотрим задачу получения только иерархии по набору элементов. Для решения задачи выберем для элементов все папки, в которых они содержатся, затем замыканием вычислим всех родителей этих папок, ну и далее выборка с обходом по 2 методу.

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

Источник

Читайте также:  Мини вырезки из дерева

Дерево справочников запросом 1с

Иногда нужно получить дерево групп справочника запросом.
Для этого удобно использовать итоги по иерархии.

Допустим у нас есть дерево:

Пусть мы выполнили какой-то запрос и получили, что нам походят элементы из некоторого списка &Список, например Д и И.

Варианты отображения иерархии

Существуют два варианта отображения иерархии, в которую попадают элементы из списка Список:

Иерархия с подчиненными

Для отображения иерархии в случае с подчиненными, нам достаточно получить родителей всех элементов, расположенных на нижнем уровне иерархии и сгруппировать их по иерархии:

ВЫБРАТЬ РАЗЛИЧНЫЕ 
Т1.Ссылка.Родитель КАК Ссылка
ИЗ
Справочник.Задача КАК Т1
ГДЕ
Т1.Ссылка В ИЕРАРХИИ(&Ссылки) И 0 В (ВЫБРАТЬ Количество(*) ИЗ Справочник.Задача КАК Т2 ГДЕ Т1.Ссылка=Т2.Родитель)
ИТОГИ ПО
Ссылка ТОЛЬКО ИЕРАРХИЯ

Для иерархии групп и элементов:

ВЫБРАТЬ РАЗЛИЧНЫЕ 
Т1.Ссылка.Родитель КАК Ссылка
ИЗ
Справочник.Задача КАК Т1
ГДЕ
Т1.Ссылка В ИЕРАРХИИ(&Ссылки) И Т1.ЭтоГруппа=ложь
ИТОГИ ПО
Ссылка ТОЛЬКО ИЕРАРХИЯ

Иерархия без подчиненных

В данном случае сложнее. 1С неправильно группирует по иерархии, она плодит дубли.
Код запроса будет таким:

ВЫБРАТЬ РАЗЛИЧНЫЕ 
Т1.Ссылка.Родитель КАК Ссылка
ИЗ
Справочник.Задача КАК Т1
ГДЕ
Т1.Ссылка В (&Ссылки)
ИТОГИ ПО
Ссылка ТОЛЬКО ИЕРАРХИЯ

Полученное дерево нужно будет обработать функцией для удаления лишних дублей:

Функция обУбратьОшибкиИтоговПоИерархии(Строки, Поле cb">Ссылка", Родитель=Неопределено) Экспорт //Убираем все элементы, равные текущему Всего=Строки.Количество(); Для Инд=1 По Всего Цикл Стр=Строки[Всего-Инд]; Зн=Стр[Поле]; Если Родитель<>Неопределено И Зн=Родитель Тогда Строки.Удалить(Стр); Иначе обУбратьОшибкиИтоговПоИерархии(Стр.Строки, Поле, Зн); КонецЕсли; КонецЦикла; КонецФункции . ВычСправочникДерево=Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией); обУбратьОшибкиИтоговПоИерархии(ВычСправочникДерево.Строки);

Источник

Оцените статью