При работе с запросами в 1С часто возникает ситуация, когда нам нужно не просто отфильтровать данные по иерархии, но и получить в результате запроса соответствие: какой именно группе из заданного списка принадлежит конкретный товар. Стандартный оператор В ИЕРАРХИИ отлично справляется с фильтрацией (условие ГДЕ), но он не возвращает саму группу, которая послужила «точкой входа» для элемента. Рассмотрим подробнее, как решить задачу сопоставления номенклатуры и её родительской группы из регистра сведений или другого источника.
Проанализируем ситуацию, описанную автором темы. Есть список товаров в документе и есть регистр сведений, в котором хранятся группы номенклатуры. Задача состоит в том, чтобы получить таблицу вида: Номенклатура | Группа из регистра. Если мы просто напишем условие ГДЕ Номенклатура В ИЕРАРХИИ (ВЫБРАТЬ Номенклатура ИЗ РегистрСведений.ТипыЦен...), система выдаст нам список товаров, но мы потеряем информацию о том, к какой конкретно группе из регистра привязан товар, особенно если в регистре указаны группы разных уровней вложенности.
Основная сложность заключается в том, что иерархия — это дерево. Один и тот же товар Тетрадь черная может входить в группу Тетради, которая в свою очередь входит в группу Канцтовары. Если в вашем регистре есть обе эти группы, возникает логическая неопределенность: какую именно группу подставить в результат? Разберем основные способы решения этой технической задачи.
Это самый быстрый и часто используемый способ, если структура справочника имеет разумное ограничение по количеству уровней вложенности (например, не более 3–5 уровней). Мы можем использовать ЛЕВОЕ СОЕДИНЕНИЕ, проверяя соответствие группы на каждом уровне иерархии.
Рассмотрим пример кода, реализующий этот подход:
ВЫБРАТЬ
ТаблицаТовары.Номенклатура КАК Номенклатура,
ТаблицаТовары.Количество,
ТаблицаТовары.Коэффициент
ПОМЕСТИТЬ ТоварыДокумента
ИЗ
&ТаблицаТовары КАК ТаблицаТовары
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ТоварыДокумента.Номенклатура КАК Номенклатура,
Группы.Номенклатура КАК ГруппаИзРегистра,
ТоварыДокумента.Количество
ИЗ
ТоварыДокумента КАК ТоварыДокумента
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ТипыЦенДляГруппНоменклатуры_КиН.СрезПоследних(&Дата, ) КАК Группы
ПО (ТоварыДокумента.Номенклатура.Родитель = Группы.Номенклатура
ИЛИ ТоварыДокумента.Номенклатура.Родитель.Родитель = Группы.Номенклатура
ИЛИ ТоварыДокумента.Номенклатура.Родитель.Родитель.Родитель = Группы.Номенклатура
ИЛИ ТоварыДокумента.Номенклатура = Группы.Номенклатура)
В данном примере мы проверяем три уровня вложенности вверх и само поле Номенклатура (на случай, если в регистре указана не группа, а сам элемент). Важный момент: если товар входит сразу в несколько групп, перечисленных в регистре (например, и в родителя, и в дедушку), в результате запроса появятся дублирующиеся строки. Чтобы этого избежать, можно использовать конструкцию ВЫБОР КОГДА для определения приоритета «ближайшего» родителя.
Если ваша задача — построить отчет, то использование «чистого» запроса будет излишне сложным. В СКД реализован мощный механизм работы с иерархией через связи наборов данных. Посмотрим, как это настроить:
Источник связи = Набор1 (Номенклатура), Приемник связи = Набор2 (Группа).Система сама выполнит рекурсивный перебор и сопоставит элементы. Это наиболее профессиональный и производительный метод для отчетности в 1С.
Если уровней вложенности много или их количество заранее неизвестно, лучше всего сначала «развернуть» иерархию групп из регистра в плоский список всех подчиненных элементов. Проанализируем этот алгоритм:
ВТ_ИсходныеГруппы.В ИЕРАРХИИ, выбираем все элементы справочника Номенклатура, которые входят в эти группы, и помещаем их в таблицу ВТ_ВсеЭлементыГрупп. При этом в таблицу сохраняем две колонки: Ссылка (самого товара) и ГруппаВладелец (из какой группы мы его «достали»).ВТ_ВсеЭлементыГрупп по обычному равенству ссылок.Рассмотрим фрагмент кода для реализации этого метода:
// Помещаем группы из регистра в ВТ
ВЫБРАТЬ
Регистр.Номенклатура КАК Группа
ПОМЕСТИТЬ ВТ_Группы
ИЗ
РегистрСведений.ТипыЦенДляГруппНоменклатуры_КиН.СрезПоследних(&Дата, ) КАК Регистр;
// Получаем все элементы этих групп
ВЫБРАТЬ
СпрНоменклатура.Ссылка КАК Товар,
ВТ_Группы.Группа КАК Группа
ПОМЕСТИТЬ ВТ_РазвернутаяИерархия
ИЗ
Справочник.Номенклатура КАК СпрНоменклатура
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Группы КАК ВТ_Группы
ПО СпрНоменклатура.Ссылка В ИЕРАРХИИ (ВТ_Группы.Группа)
ГДЕ
НЕ СпрНоменклатура.ЭтоГруппа;
Этот способ позволяет избежать «лесенки» из Родитель.Родитель... и работает корректно при любой глубине дерева справочника.
Выясним причину возможных замедлений: оператор В ИЕРАРХИИ на уровне СУБД трансформируется в достаточно тяжелый подзапрос. Если в документе тысячи строк, а в регистре сотни групп, производительность может упасть. В таких случаях:
Родитель, если конфигурация позволяет вносить изменения в структуру справочника.Тетрадь входит в группу Бумажная продукция и в группу Школьные товары, и обе они есть в регистре, запрос вернет две строки для одной тетради. Используйте СГРУППИРОВАТЬ ПО или функцию МАКСИМУМ, чтобы оставить только одну связь.Таким образом, выбор метода зависит от конкретной задачи: для простых случаев подойдет соединение по родителям через точку, для сложных отчетов — СКД, а для универсальных механизмов — предварительное развертывание иерархии во временные таблицы.