gifts2017

Мои шаблоны. Нахождение всех родителей в запросе

Опубликовал Пишу код как картины (yurii_host) в раздел Программирование - Практика программирования

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

Часто требуется получить всех родителей для группы элементов. Не люблю использовать циклы и рекурсию без крайней необходмости. Давно хотелось найти универсальный способ, желательно через запрос. 

Решение с сайта 1с обладает существенным ограничением: нужно заранее знать количество уровней. 

Первый способ лишен этого недостатка, однако имеет другие:

1. Результат запроса нельзя выгрузить во временную таблицу, т.к. запрос строится по итогам

2. В результирующем запросе присутствуют задублированные строки, поэтому нужно выполнять дополнительную обработку, чтобы устранить этот эффект.

Однако, я часто использую именно этот способ, за неимением более простого решения. Он подходит для небольших по размеру иерархических справочников.

ВЫБРАТЬ 
    Склады.Ссылка КАК Ссылка,
    Склады.Ссылка.Ссылка КАК Родитель
ИЗ
    Справочник.Склады КАК Склады
ИТОГИ ПО
    Ссылка,
    Родитель ИЕРАРХИЯ

Второй способ является модификацией метода, предложенного 1с. Недостатком этого метода:

1. Низкая производительность, т.к. выполняется большое количество соединений, а также используется условие ИЛИ

2. Жестко задается количество уровней

Поэтому он также подходит только для справочников с небольшим количеством элементов

ВЫБРАТЬ
	СкладыИерархия.Ссылка КАК СкладИерахия,
	Склады.Ссылка КАК Склад
ИЗ Справочник.Склады КАК Склады
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Склады КАК СкладыИерархия
	ПО (Склады.Ссылка = СкладыИерархия.Ссылка
	ИЛИ Склады.Родитель = СкладыИерархия.Ссылка
	ИЛИ Склады.Родитель.Родитель = СкладыИерархия.Ссылка
	ИЛИ Склады.Родитель.Родитель.Родитель = СкладыИерархия.Ссылка
	ИЛИ Склады.Родитель.Родитель.Родитель.Родитель = СкладыИерархия.Ссылка
	ИЛИ Склады.Родитель.Родитель.Родитель.Родитель.Родитель = СкладыИерархия.Ссылка
	ИЛИ Склады.Родитель.Родитель.Родитель.Родитель.Родитель.Родитель = СкладыИерархия.Ссылка
	ИЛИ Склады.Родитель.Родитель.Родитель.Родитель.Родитель.Родитель.Родитель = СкладыИерархия.Ссылка)

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Константин Юрин (kostyaomsk) 05.08.15 07:17
Что-то сильно просто. Такие задачи дают на собеседованиях в крутые фирмы. Из разряда "По регистру Счета учета номенклатуры (УТ, БП) для списка входной номенклатуры если не указаны счета смотреть счета по группе номенклатуры и так до самого верха".
Самое веселое, что если даже такие задачи на фрилансе или в ИТ-отделе редко попадаются все-равно для быстроты решаются кодом. Запросом - надо как-то склеивать или специально искать - только если оправдано по большой загрузке. И так по всем случаям.
Также часть в практике задача получения всех подчиненных документов или документов-оснований. Вот эти примеры более полезны чтоб иметь библиотеку под рукой.
2. Константин Юрин (kostyaomsk) 05.08.15 07:22
К тому же конструкция запроса В ИЕРАРХИИ(&параметр) сама очень не оптимально. Используется только для простоты.
3. Алексей Лапицкий (Lapitskiy) 05.08.15 08:08
(1) kostyaomsk, точно, на собеседовании почему то всех интересует "виртуальный конь в вакууме"
4. Антон Рощин (wolfsoft) 05.08.15 08:24
Решение с сайта 1с обладает существенным ограничением: нужно заранее знать количество уровней.


Как бы нет. Просто выборка идёт по 5 уровней, но само количество уровней не ограничено. Другое дело, что запрос в цикле.
6. Игорь Steelvan (Steelvan) 05.08.15 09:39
// По входящей ссылке формирует дерево подчиненных объектов из элементов Дерева процесов.
/
/
Процедура СформироватьДеревоОбъектаСПодчиненными(ДеревоОбъектаСПодчиненными = Неопределено, СсылкаВладельца, тзИсточник = Неопределено) Экспорт
	
	// Создание возвращаемого дерева
	Если ДеревоОбъектаСПодчиненными = Неопределено тогда
		ДеревоОбъектаСПодчиненными  = Новый ДеревоЗначений;
		ДеревоОбъектаСПодчиненными.Колонки.Добавить("СсылкаЭлемента");
		ДеревоОбъектаСПодчиненными.Колонки.Добавить("Наименование");
		ДеревоОбъектаСПодчиненными.Колонки.Добавить("СтрокаНавигационнойСсылкиНаКартинку");
	КонецЕсли;
	
	// Получение данных для исходной строки
	Если тзИсточник = Неопределено Тогда
		
		// Получаю тз, отсортированную по номерам из регистра сведений
		лпЗапросЭлементов					   	  = Новый Запрос;
		лпЗапросЭлементов.Текст = "ВЫБРАТЬ
		|	осЭлементыДереваПроцессов.СсылкаВладельца,
		|	осЭлементыДереваПроцессов.Ссылка КАК СсылкаЭлемента,
		|	осЭлементыДереваПроцессов.Ссылка.Наименование КАК Наименование,
		|	осЭлементыДереваПроцессов.КартинкаЭлемента.СтрокаНавигационнойСсылкиНаКартинку КАК СтрокаНавигационнойСсылкиНаКартинку
		|ИЗ
		|	Справочник.осЭлементыДереваПроцессов КАК осЭлементыДереваПроцессов
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.осНумерацияЭлементовДП КАК осНумерацияЭлементовДП
		|		ПО (осНумерацияЭлементовДП.СсылкаЭлемента = осЭлементыДереваПроцессов.Ссылка)
		|			И (осНумерацияЭлементовДП.СсылкаВладельца = осЭлементыДереваПроцессов.СсылкаВладельца)
		|ГДЕ
		|	осЭлементыДереваПроцессов.ПометкаУдаления = ЛОЖЬ
		|
		|ДЛЯ ИЗМЕНЕНИЯ
		|	Справочник.осЭлементыДереваПроцессов,
		|	РегистрСведений.осНумерацияЭлементовДП
		|
		|УПОРЯДОЧИТЬ ПО
		|	осНумерацияЭлементовДП.НомерПорядка";
		тзИсточник = лпЗапросЭлементов.Выполнить().Выгрузить();
	КонецЕсли;
	
	// Добавление строки в возвращаемое дерево
	СтрокаСсылкиЭлементаВтзИсточник = тзИсточник.Найти(СсылкаВладельца, "СсылкаЭлемента");
	Если СтрокаСсылкиЭлементаВтзИсточник <> Неопределено тогда
		НоваяСтрокаДерева = ДеревоОбъектаСПодчиненными.Строки.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрокаДерева, СтрокаСсылкиЭлементаВтзИсточник);
	КонецЕсли;
	
	// Поиск подчиненных строк
	мПодчиненныеСтрокиВтзИсточник = тзИсточник.НайтиСтроки(Новый Структура("СсылкаВладельца", СсылкаВладельца));
	Для каждого ТекПодчиненнаяСтрока из мПодчиненныеСтрокиВтзИсточник Цикл
		СсылкаПодчиненногоЭлемента = ТекПодчиненнаяСтрока.СсылкаЭлемента;
		СформироватьДеревоОбъектаСПодчиненными(НоваяСтрокаДерева, СсылкаПодчиненногоЭлемента, тзИсточник);
	КонецЦикла;
	
КонецПроцедуры
...Показать Скрыть
7. Игорь Steelvan (Steelvan) 05.08.15 09:42
Пример получения дерева. В качестве группирующего поля для дерева используется СсылкаВладельца. На порядок быстрее чем &ВИерархии. Работает без заранее определенного количества уровней.
8. Александр Черкасов (CherAl) 05.08.15 10:02
А такой вариант?
Родитель = Объект.СсылкаНаОбъект.Родитель;
	Пока Родитель <> Справочники[Родитель.метаданные().Имя].ПустаяСсылка() Цикл
		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = (Строка(Родитель));
		Сообщение.Сообщить();
		Родитель = Родитель.Родитель; 
	КонецЦикла;
...Показать Скрыть
9. Анянов Михаил (insurgut) 05.08.15 10:07
Запрос выдает дважды сам элемент, так что хотя бы так:
ВЫБРАТЬ
	Склады.Ссылка.Ссылка КАК Родитель
ИЗ
	Справочник.Склады КАК Склады
ГДЕ
	Склады.Ссылка = &МойСклад
ИТОГИ ПО
	Родитель ТОЛЬКО ИЕРАРХИЯ
...Показать Скрыть
10. Анянов Михаил (insurgut) 05.08.15 10:09
(8) CherAl, приведу простой пример, в обработке заполняется 100 000 строк. При заполнении было условие "Если НЕ РезультатЗапроса.Номенклатура.ЭтоГруппа Тогда"... В итоге обработка открывалась несколько минут. Если в запросе сразу поставить это условие - то секунды.
11. Пишу код как картины (yurii_host) 05.08.15 10:21
(10) insurgut, согласен. Но в статье я указал, что метод "подходит для небольших по размеру иерархических справочников."
12. Пишу код как картины (yurii_host) 05.08.15 10:23
(8) CherAl, пусть в справочнике 100 элементов и 5 уровней иерархии. Сколько будет обращений к базе с Вашим вариантом?
13. Пишу код как картины (yurii_host) 05.08.15 10:24
(9) insurgut, это просто заготовка. Никто не запрещает накладывать фильтры
14. Пишу код как картины (yurii_host) 05.08.15 10:39
(3) Lapitskiy, иногда задачи, которые дают на собеседовании пригождаются в реальной практике, а иногда на собеседовании дают задачки из практики
15. борян петров (TODD22) 05.08.15 11:58
(14) yurii_host,
иногда задачи, которые дают на собеседовании пригождаются в реальной практике, а иногда на собеседовании дают задачки из практики

Нужно оценивать общий уровень а не умение решать одну задачу которая "иногда пригождается". Для всего с приставкой "иногда" есть гугл.
16. Пишу код как картины (yurii_host) 05.08.15 16:47
Всем большое спасибо за критику.

Может ли кто-нибудь подсказать вариант, чтобы данная задача решалась исключительно средствами запросов с возможностью помещения результата во временную таблицу. Например, это было бы очень полезно для формирования отчетов на СКД.

Решения с использованием встроенного языка очевидны, просьба их не предлагать.
17. Сергей (ildarovich) 05.08.15 20:32
(16) yurii_host, в комментарии (5) вам правильно ответили, где можно посмотреть возможное решение. Это, например, статья "Транзитивное замыкание запросом".

Если вы уже видели эту статью или статью Уровни, глубина, прародители, циклы и аналоги запросом, но не посчитали приведенное там решение подходящим, то, возможно, вас смутило то, что там приведены не сами запросы, а функции для их построения. Но на самом деле я предполагал очевидным то, что вместо функции можно использовать заранее построенный ей запрос.

Например, для справочника "Номенклатура" по принципам, описанным в статье, можно использовать запрос:
ВЫБРАТЬ
	Номенклатура.Родитель КАК НачалоДуги,
	Номенклатура.Ссылка КАК КонецДуги
ПОМЕСТИТЬ ЗамыканияДлины1
ИЗ
	Справочник.Номенклатура КАК Номенклатура
ГДЕ
	Номенклатура.Родитель <> ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)

ОБЪЕДИНИТЬ

ВЫБРАТЬ
	Номенклатура.Ссылка,
	Номенклатура.Ссылка
ИЗ
	Справочник.Номенклатура КАК Номенклатура
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПерваяДуга.НачалоДуги,
	ВтораяДуга.КонецДуги
ПОМЕСТИТЬ ЗамыканияДлины2
ИЗ
	ЗамыканияДлины1 КАК ПерваяДуга
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЗамыканияДлины1 КАК ВтораяДуга
		ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПерваяДуга.НачалоДуги,
	ВтораяДуга.КонецДуги
ПОМЕСТИТЬ ЗамыканияДлины4
ИЗ
	ЗамыканияДлины2 КАК ПерваяДуга
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЗамыканияДлины2 КАК ВтораяДуга
		ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПерваяДуга.НачалоДуги,
	ВтораяДуга.КонецДуги
ПОМЕСТИТЬ ЗамыканияДлины8
ИЗ
	ЗамыканияДлины4 КАК ПерваяДуга
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЗамыканияДлины4 КАК ВтораяДуга
		ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПерваяДуга.НачалоДуги,
	ВтораяДуга.КонецДуги
ПОМЕСТИТЬ ЗамыканияДлины16
ИЗ
	ЗамыканияДлины8 КАК ПерваяДуга
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЗамыканияДлины8 КАК ВтораяДуга
		ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПерваяДуга.НачалоДуги,
	ВтораяДуга.КонецДуги
ПОМЕСТИТЬ ЗамыканияДлины32
ИЗ
	ЗамыканияДлины16 КАК ПерваяДуга
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ЗамыканияДлины16 КАК ВтораяДуга
		ПО ПерваяДуга.КонецДуги = ВтораяДуга.НачалоДуги
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Замыкания.НачалоДуги КАК Предок,
	Замыкания.КонецДуги КАК Потомок
ИЗ
	ЗамыканияДлины32 КАК Замыкания
ГДЕ
	Замыкания.НачалоДуги <> Замыкания.КонецДуги
...Показать Скрыть


Здесь предполагается, что справочник "Номенклатура" имеет НЕ БОЛЕЕ 32-х уровней вложенности (в жизни вряд ли бывают справочники большей глубины вложенности). А повторяющихся фрагментов в запросе всего 5! Если вдруг уровней больше, до тысячи, например, то можно добавить еще всего 5 однотипных фрагментов в предлагаемый пакетный запрос.
Приведенный запрос - это не запрос в цикле. Он строится не динамически, а может быть записан полностью заранее. Его можно использовать на любом этапе более сложного запроса. И по структуре он достаточно прост.
Somebody1; MRAK; Il; yurii_host; +4 Ответить 1
18. Пишу код как картины (yurii_host) 05.08.15 23:34
(17) ildarovich, интересный прием. В виде запроса идея намного прозрачнее. Большой плюс, что обращение к самому справочнику только в самом первом запросе, остальное - переливание между временными таблицами.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа