Доброго времени суток!
Часто при формировании отчетов необходимо вывести информацию по справочнику в разрезе иерархии, но работать с таким отчетом не всегда удобно, т.к. уровень вложенности различных элементов справочника может значительно отличаться. Для формирования отчетов по справочнику "Номенклатура" я разработал функцию, позволяющую рекурсивно получить таблицу значений с произвольным количеством родителей элементов справочника.
Возможно, что кому-то это пригодится и сэкономит некоторое количество времени.
Спасибо за внимание :)
P.S. советы/замечания по реализации алгоритма приветствуются в комментариях.
upd (29.03.2019): По совету из комментариев добавил версию без рекурсии, она работает в несколько раз быстрее. Тем не менее версия с рекурсией определенно имеет право на жизнь, т.к. в такой функции можно помимо получения иерархии добавить любые арифметические операции, вызовы сторонних функций и т.д. Код является более читабельным и "удобоваримым".
&НаСервере
// + //infostart.ru/profile/504528/ 20190328
// Параметры:
// фМассивТаблицаНоменклатуры - Массив, заполненный значениями справочника "Номенклатура", либо Таблица значений с колонкой "Номенклатура"
// фКоличествоРодителей - целое Число, определяет какое количество родителей "верхнего" уровня необходимо получить
// УровеньРекурсии - технический параметр, отражающий текущий уровень рекурсии - передавать не нужно!
//
// Функция возвращает таблицу значений с колонками "Номенклатура", "НоменалутраРодитель1", ... , "НоменклатураРодительN", где N - значение параметра "фКоличествоРодителей"
// - //infostart.ru/profile/504528/ 20190328
Функция ПолучитьТаблицуРодителейНоменклатурыРекурсией(фМассивТаблицаНоменклатуры = Неопределено, фКоличествоРодителей = 2, УровеньРекурсии = 0) Экспорт
Если фМассивТаблицаНоменклатуры = Неопределено Тогда
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| сНоменклатура.Ссылка КАК Номенклатура
|ИЗ
| Справочник.Номенклатура КАК сНоменклатура
|ГДЕ
| сНоменклатура.ЭтоГруппа = ЛОЖЬ";
фМассивТаблицаНоменклатуры = Запрос.Выполнить().Выгрузить();
КонецЕсли;
Отказ = Ложь;
Если фКоличествоРодителей < 1 Тогда
Отказ = Истина;
КонецЕсли;
Если Не Отказ Тогда
Если ТипЗнч(фМассивТаблицаНоменклатуры) = Тип("Массив") Тогда
ТаблицаНоменклатуры = Новый ТаблицаЗначений;
ТаблицаНоменклатуры.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТаблицаНоменклатуры.ЗагрузитьКолонку(фМассивТаблицаНоменклатуры, "Номенклатура");
ТаблицаНоменклатуры.Индексы.Добавить("Номенклатура");
Для индекс = 0 по фКоличествоРодителей Цикл
ТаблицаНоменклатуры.Колонки.Добавить("НоменклатураРодитель" + индекс, Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
КонецЦикла;
ТаблицаНоменклатуры.ЗагрузитьКолонку(фМассивТаблицаНоменклатуры, "НоменклатураРодитель0");
ИначеЕсли ТипЗнч(фМассивТаблицаНоменклатуры) = Тип("ТаблицаЗначений") Тогда
ТаблицаНоменклатуры = фМассивТаблицаНоменклатуры;
Если ТаблицаНоменклатуры.Колонки.Найти("Номенклатура") = Неопределено Тогда
Отказ = Истина;
КонецЕсли;
Если Не Отказ Тогда
ТаблицаНоменклатуры.Индексы.Добавить("Номенклатура");
Для индекс = 0 по фКоличествоРодителей Цикл
Если ТаблицаНоменклатуры.Колонки.Найти("НоменклатураРодитель" + индекс) = Неопределено Тогда
ТаблицаНоменклатуры.Колонки.Добавить("НоменклатураРодитель" + индекс, Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Если индекс = 0 Тогда
ТаблицаНоменклатуры.ЗагрузитьКолонку(ТаблицаНоменклатуры.ВыгрузитьКолонку("Номенклатура"), "НоменклатураРодитель0");
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Иначе
Отказ = Истина;
КонецЕсли;
КонецЕсли;
Если Отказ Тогда
Возврат фМассивТаблицаНоменклатуры;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| тзНоменклатура.Номенклатура КАК Номенклатура,
| тзНоменклатура.НоменклатураРодитель0 КАК НоменклатураРодитель0
|ПОМЕСТИТЬ втДанные
|ИЗ
| &тзНоменклатура КАК тзНоменклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗРЕШЕННЫЕ
| втДанные.Номенклатура КАК Номенклатура,
| втДанные.НоменклатураРодитель0 КАК НоменклатураРодитель,
| ЕСТЬNULL(сНоменклатура.Родитель, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК НовыйРодитель
|ИЗ
| втДанные КАК втДанные
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК сНоменклатура
| ПО втДанные.НоменклатураРодитель0 = сНоменклатура.Ссылка
|ГДЕ
| втДанные.НоменклатураРодитель0 <> ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
|;
|
|////////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ втДанные";
Запрос.УстановитьПараметр("тзНоменклатура", ТаблицаНоменклатуры);
Выборка = Запрос.Выполнить().Выбрать();
КонецРекурсии = Истина;
Если УровеньРекурсии < 15 Тогда // Защита от бесконечного цикла
Пока Выборка.Следующий() Цикл
тзСтроки = ТаблицаНоменклатуры.НайтиСтроки(Новый Структура("Номенклатура", Выборка.Номенклатура));
Для Каждого тзСтрока из тзСтроки Цикл
Если УровеньРекурсии > 0 Тогда // Нет смысла перезаполнять родителей на первом этапе рекурсии, при малой вложенности справочника нельзя копировать значение НоменклатураРодитель0
индекс = фКоличествоРодителей;
Пока индекс > 0 Цикл
тзСтрока["НоменклатураРодитель" + индекс] = тзСтрока["НоменклатураРодитель" + (индекс - 1)];
индекс = индекс - 1;
КонецЦикла;
КонецЕсли;
тзСтрока.НоменклатураРодитель0 = Выборка.НовыйРодитель;
Если ЗначениеЗаполнено(тзСтрока.НоменклатураРодитель0) Тогда
КонецРекурсии = Ложь;
КонецЕсли;
//Здесь можно что-то вычислять, вызывать доп. функции и т.д.
КонецЦикла;
КонецЦикла;
КонецЕсли;
Если КонецРекурсии Тогда
ТаблицаНоменклатуры.Колонки.Удалить("НоменклатураРодитель0");
Иначе
УровеньРекурсии = УровеньРекурсии + 1;
ТаблицаНоменклатуры = ПолучитьТаблицуРодителейНоменклатурыРекурсией(ТаблицаНоменклатуры, фКоличествоРодителей, УровеньРекурсии);
КонецЕсли;
Возврат ТаблицаНоменклатуры;
КонецФункции
&НаСервере
// + //infostart.ru/profile/504528/ 20190329
// Параметры:
// фМассивТаблицаНоменклатуры - Массив, заполненный значениями справочника "Номенклатура", либо Таблица значений с колонкой "Номенклатура"
// фКоличествоРодителей - целое Число, определяет какое количество родителей "верхнего" уровня необходимо получить
// МаксимальныйУровеньВложенности - технический параметр, отражающий глубину построения иерархии
//
// Функция возвращает таблицу значений с колонками "Номенклатура", "НоменалутраРодитель1", ... , "НоменклатураРодительN", где N - значение параметра "фКоличествоРодителей"
// - //infostart.ru/profile/504528/ 20190329
Функция ПолучитьТаблицуРодителейНоменклатурыЗапросом(фМассивТаблицаНоменклатуры = Неопределено, фКоличествоРодителей = 2, МаксимальныйУровеньВложенности = 10) Экспорт
Отказ = Ложь;
Если фКоличествоРодителей < 1 Тогда
Отказ = Истина;
КонецЕсли;
Если МаксимальныйУровеньВложенности < фКоличествоРодителей Тогда
Отказ = Истина;
КонецЕсли;
Если Не Отказ Тогда
Запрос = Новый Запрос;
Если ТипЗнч(фМассивТаблицаНоменклатуры) = Тип("Массив") Тогда
ЗапросДанных = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| сНоменклатура.Ссылка КАК Номенклатура
|ПОМЕСТИТЬ втДанные
|ИЗ
| Справочник.Номенклатура КАК сНоменклатура
|ГДЕ
| сНоменклатура.Ссылка В (&тзНоменклатура)
|;";
Запрос.УстановитьПараметр("тзНоменклатура", фМассивТаблицаНоменклатуры);
ИначеЕсли ТипЗнч(фМассивТаблицаНоменклатуры) = Тип("ТаблицаЗначений") Тогда
Если фМассивТаблицаНоменклатуры.Колонки.Найти("Номенклатура") = Неопределено Тогда
Отказ = Истина;
КонецЕсли;
ЗапросДанных = "ВЫБРАТЬ";
ЗапросПолей = "";
Для каждого тзКолонка из фМассивТаблицаНоменклатуры.Колонки Цикл
ЗапросПолей = ЗапросПолей + "
| тзНоменклатура." + тзКолонка.Имя + " КАК " + тзКолонка.Имя + ",";
КонецЦикла;
ЗапросДанных = ЗапросДанных + Лев(ЗапросПолей, СтрДлина(ЗапросПолей) - 1) + "
|ПОМЕСТИТЬ втДанные
|ИЗ
| &тзНоменклатура КАК тзНоменклатура
|;";
Запрос.УстановитьПараметр("тзНоменклатура", фМассивТаблицаНоменклатуры);
ИначеЕсли фМассивТаблицаНоменклатуры = Неопределено Тогда
ЗапросДанных = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| сНоменклатура.Ссылка КАК Номенклатура
|ПОМЕСТИТЬ втДанные
|ИЗ
| Справочник.Номенклатура КАК сНоменклатура
|ГДЕ
| сНоменклатура.ЭтоГруппа = ЛОЖЬ
|;";
Иначе
Отказ = Истина;
КонецЕсли;
КонецЕсли;
Если Отказ Тогда
Возврат фМассивТаблицаНоменклатуры;
КонецЕсли;
ЗапросПодвал = "";
ЗапросТекст = ЗапросДанных + "
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗРЕШЕННЫЕ
| сНоменклатура.Ссылка КАК Номенклатура,
| сНоменклатура.Родитель КАК Родитель
|ПОМЕСТИТЬ втРодители
|ИЗ
| Справочник.Номенклатура КАК сНоменклатура
|ГДЕ
| сНоменклатура.ЭтоГруппа = ИСТИНА
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| втРодители.Номенклатура КАК Номенклатура
|ПОМЕСТИТЬ втРодители1
|ИЗ
| втРодители КАК втРодители
|ГДЕ
| втРодители.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
|;
|";
Для индекс = 2 по МаксимальныйУровеньВложенности Цикл
ЗапросТекст = ЗапросТекст + "
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| втРодители.Номенклатура КАК Номенклатура,
| втРодители.Родитель КАК Родитель
|ПОМЕСТИТЬ втРодители" + индекс + "
|ИЗ
| втРодители КАК втРодители
|ГДЕ
| втРодители.Родитель В
| (ВЫБРАТЬ
| втРодители" + (индекс - 1) + ".Номенклатура
| ИЗ
| втРодители" + (индекс - 1) + " КАК втРодители" + (индекс - 1) + ")
|;
|";
ЗапросПодвал = ЗапросПодвал + "
|;
|
|////////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ втРодители" + индекс;
КонецЦикла;
ЗапросТекст = ЗапросТекст + "
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ РАЗРЕШЕННЫЕ";
Если ТипЗнч(фМассивТаблицаНоменклатуры) = Тип("ТаблицаЗначений") Тогда
ЗапросТекст = ЗапросТекст +
ЗапросПолей + "
| втРодители1.Номенклатура КАК НоменклатураРодитель1";
Иначе
ЗапросТекст = ЗапросТекст + "
| тзНоменклатура.Номенклатура КАК Номенклатура,
| втРодители1.Номенклатура КАК НоменклатураРодитель1";
КонецЕсли;
Для индекс = 2 по фКоличествоРодителей Цикл
ЗапросТекст = ЗапросТекст + ",
| втРодители" + индекс + ".Номенклатура КАК НоменклатураРодитель" + индекс;
КонецЦикла;
ЗапросТекст = ЗапросТекст + "
|ИЗ
| втДанные КАК тзНоменклатура
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК сНоменклатура
| ПО тзНоменклатура.Номенклатура = сНоменклатура.Ссылка";
индекс = МаксимальныйУровеньВложенности;
Пока индекс > 0 Цикл
Если индекс = МаксимальныйУровеньВложенности Тогда
ЗапросТекст = ЗапросТекст + "
| ЛЕВОЕ СОЕДИНЕНИЕ втРодители" + индекс + " КАК втРодители" + индекс + "
| ПО (сНоменклатура.Родитель = втРодители" + индекс + ".Номенклатура)";
Иначе
ЗапросТекст = ЗапросТекст + "
| ЛЕВОЕ СОЕДИНЕНИЕ втРодители" + индекс + " КАК втРодители" + индекс + "
| ПО (ЕСТЬNULL(втРодители" + (индекс + 1) + ".Родитель, сНоменклатура.Родитель) = втРодители" + индекс + ".Номенклатура)";
КонецЕсли;
индекс = индекс - 1;
КонецЦикла;
ЗапросТекст = ЗапросТекст + ЗапросПодвал + "
|;
|
|////////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ втРодители1
|;
|
|////////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ втРодители
|;
|
|////////////////////////////////////////////////////////////////////////////////
|УНИЧТОЖИТЬ втДанные";
Запрос.Текст = ЗапросТекст;
Сообщить(ЗапросТекст);
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
ВЫБРАТЬ РАЗРЕШЕННЫЕ
сНоменклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ втДанные
ИЗ
Справочник.Номенклатура КАК сНоменклатура
ГДЕ
сНоменклатура.ЭтоГруппа = ЛОЖЬ
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
сНоменклатура.Ссылка КАК Номенклатура,
сНоменклатура.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители
ИЗ
Справочник.Номенклатура КАК сНоменклатура
ГДЕ
сНоменклатура.ЭтоГруппа = ИСТИНА
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура
ПОМЕСТИТЬ втРодители1
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители2
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители1.Номенклатура
ИЗ
втРодители1 КАК втРодители1)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители3
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители2.Номенклатура
ИЗ
втРодители2 КАК втРодители2)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители4
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители3.Номенклатура
ИЗ
втРодители3 КАК втРодители3)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители5
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители4.Номенклатура
ИЗ
втРодители4 КАК втРодители4)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители6
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители5.Номенклатура
ИЗ
втРодители5 КАК втРодители5)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители7
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители6.Номенклатура
ИЗ
втРодители6 КАК втРодители6)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители8
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители7.Номенклатура
ИЗ
втРодители7 КАК втРодители7)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители9
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители8.Номенклатура
ИЗ
втРодители8 КАК втРодители8)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
втРодители.Номенклатура КАК Номенклатура,
втРодители.Родитель КАК Родитель
ПОМЕСТИТЬ втРодители10
ИЗ
втРодители КАК втРодители
ГДЕ
втРодители.Родитель В
(ВЫБРАТЬ
втРодители9.Номенклатура
ИЗ
втРодители9 КАК втРодители9)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗРЕШЕННЫЕ
тзНоменклатура.Номенклатура КАК Номенклатура,
втРодители1.Номенклатура КАК НоменклатураРодитель1,
втРодители2.Номенклатура КАК НоменклатураРодитель2
ИЗ
втДанные КАК тзНоменклатура
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК сНоменклатура
ПО тзНоменклатура.Номенклатура = сНоменклатура.Ссылка
ЛЕВОЕ СОЕДИНЕНИЕ втРодители10 КАК втРодители10
ПО (сНоменклатура.Родитель = втРодители10.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители9 КАК втРодители9
ПО (ЕСТЬNULL(втРодители10.Родитель, сНоменклатура.Родитель) = втРодители9.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители8 КАК втРодители8
ПО (ЕСТЬNULL(втРодители9.Родитель, сНоменклатура.Родитель) = втРодители8.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители7 КАК втРодители7
ПО (ЕСТЬNULL(втРодители8.Родитель, сНоменклатура.Родитель) = втРодители7.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители6 КАК втРодители6
ПО (ЕСТЬNULL(втРодители7.Родитель, сНоменклатура.Родитель) = втРодители6.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители5 КАК втРодители5
ПО (ЕСТЬNULL(втРодители6.Родитель, сНоменклатура.Родитель) = втРодители5.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители4 КАК втРодители4
ПО (ЕСТЬNULL(втРодители5.Родитель, сНоменклатура.Родитель) = втРодители4.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители3 КАК втРодители3
ПО (ЕСТЬNULL(втРодители4.Родитель, сНоменклатура.Родитель) = втРодители3.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители2 КАК втРодители2
ПО (ЕСТЬNULL(втРодители3.Родитель, сНоменклатура.Родитель) = втРодители2.Номенклатура)
ЛЕВОЕ СОЕДИНЕНИЕ втРодители1 КАК втРодители1
ПО (ЕСТЬNULL(втРодители2.Родитель, сНоменклатура.Родитель) = втРодители1.Номенклатура)
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители2
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители3
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители4
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители5
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители6
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители7
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители8
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители9
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители10
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители1
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втРодители
;
////////////////////////////////////////////////////////////////////////////////
УНИЧТОЖИТЬ втДанные