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