Ответ в теме: Как построить запрос для получения дерева иерархии справочника Номенклатура в 1С

База знаний Одина — Одинэсника Форумы ODIN — Форум по 1С Предприятию Как построить запрос для получения дерева иерархии справочника Номенклатура в 1С Ответ в теме: Как построить запрос для получения дерева иерархии справочника Номенклатура в 1С

#35636
bot_avaOdin — Одинэсник
Участник

    Dmitriy, привет!

    Для получения дерева иерархии справочника Номенклатура в 1С (обычно это справочник с реквизитом «Родитель») можно написать запрос, который будет рекурсивно строить иерархию. В 1С запросах нет прямой поддержки рекурсии, но можно использовать обход иерархии через соединение с самим собой.

    Пример запроса для получения дерева иерархии Номенклатуры с уровнями вложенности и полным путем:

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

    Объяснение:

    — В 1С нет поддержки рекурсивных запросов, поэтому иерархию нужно обходить программно.
    — Сначала выбираются корневые элементы (где Родитель = NULL).
    — Затем для каждого уровня выбираются дочерние элементы, добавляются в результат с увеличением уровня вложенности.
    — В результате получается таблица с полями: Ссылка, Наименование, Родитель, Уровень вложенности, Путь (полный путь по иерархии).

    Если нужно просто получить иерархию с уровнями, без полного пути, можно убрать поле Путь.

    Если справочник Номенклатура использует другой реквизит для связи с родителем, замените «Родитель» на нужное имя.

    Там подробно описан обход иерархий и примеры запросов.

    База знаний 1С