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