В практике разработки на платформе 1С:Предприятие часто возникает задача подбора аналогов товаров при оформлении документов отгрузки. Нередко в учетных системах или учебных задачах (например, в типовых билетах экзамена «1С:Специалист») структура хранения взаимозаменяемых товаров организуется средствами самого справочника Номенклатура с включенной иерархией элементов. В такой модели считается, что родительский элемент задает основную позицию, а все подчиненные элементы этой ветки являются ее аналогами.
При попытке связать строки табличной части документа со всеми подчиненными элементами иерархии через один запрос разработчики часто сталкиваются с тем, что вместо красивой группировки товары выводятся общим неструктурированным списком, а в колонках базовой номенклатуры появляются пустые значения (NULL). Разберем причины такого поведения платформы и подробно рассмотрим работающие способы решения задачи.
Рассмотрим исходный запрос, в котором была сделана попытка связать номенклатуру документа с подчиненными элементами:
ВЫБРАТЬ
ДокТЧ.Номенклатура КАК Номенклатура,
Ном.Ссылка КАК Аналог
ИЗ
ДокТЧ КАК ДокТЧ
ПОЛНОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном
ПО ДокТЧ.Номенклатура = Ном.Ссылка
ГДЕ
Ном.Ссылка В ИЕРАРХИИ
(ВЫБРАТЬ
ДокТч.Номенклатура
ИЗ
ДокТч)
Проанализируем две ключевые логические ошибки, допущенные в этом коде:
ПО ДокТЧ.Номенклатура = Ном.Ссылка пытается связать строку документа только с точно таким же элементом справочника. Для дочерних элементов (собственно аналогов) ссылка не совпадает с ссылкой родителя из табличной части документа. Из-за использования ПОЛНОЕ СОЕДИНЕНИЕ (или ЛЕВОЕ СОЕДИНЕНИЕ) платформа присоединяет подчиненные строки без сопоставления с левой таблицей, заполняя поле ДокТЧ.Номенклатура значением NULL.ГДЕ Ном.Ссылка В ИЕРАРХИИ (...) лишь отсекает лишние элементы справочника, которые не входят ни в одну из веток указанных в документе товаров. Однако этот фильтр никак не указывает СУБД, к какой именно строке документа относится конкретный найденный подчиненный элемент. В результате выборка аналогов смешивается в единый список.Главное ограничение платформы 1С заключается в том, что оператор В ИЕРАРХИИ категорически запрещено использовать внутри предложения ПО при соединении таблиц. Язык запросов 1С разрешает применять его только в блоках ГДЕ и ИМЕЮЩИЕ. Поэтому напрямую соединить таблицы по условию вхождения в ветку невозможно. Рассмотрим практические пути обхода этого ограничения.
Если глубина вложенности иерархии элементов заранее известна и ограничена (например, до трех или четырех уровней), наиболее быстрым и эффективным приемом является соединение по реквизиту Родитель с проверкой всех возможных уровней иерархии.
Разберем готовый пример запроса:
ВЫБРАТЬ
ДокТЧ.Номенклатура КАК БазоваяНоменклатура,
Аналоги.Ссылка КАК НоменклатураАналог,
ВЫБОР
КОГДА Аналоги.Ссылка = ДокТЧ.Номенклатура
ТОГДА 1
ИНАЧЕ 2
КОНЕЦ КАК Приоритет
ИЗ
ДокТЧ КАК ДокТЧ
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Аналоги
ПО ДокТЧ.Номенклатура = Аналоги.Ссылка
ИЛИ ДокТЧ.Номенклатура = Аналоги.Родитель
ИЛИ ДокТЧ.Номенклатура = Аналоги.Родитель.Родитель
ИЛИ ДокТЧ.Номенклатура = Аналоги.Родитель.Родитель.Родитель
УПОРЯДОЧИТЬ ПО
БазоваяНоменклатура,
Приоритет,
НоменклатураАналог
ИТОГИ ПО
БазоваяНоменклатура
Посмотрим на логику работы этого решения:
ЛЕВОЕ СОЕДИНЕНИЕ от таблицы документа. Это гарантирует, что даже если у позиции нет подчиненных аналогов, сам исходный товар останется в выборке.ПО задается составное условие через оператор ИЛИ. Строка документа связывается как с самой собой (для получения остатков исходной позиции с наивысшим приоритетом), так и со своими потомками первого, второго и третьего уровней.ДокТЧ.Номенклатура каждый найденный аналог жестко привязан к конкретному товару из документа. Секция ИТОГИ ПО или последующая группировка выборки в коде 1С позволяет корректно обойти аналоги раздельно по каждой исходной строке.Если цепочка обращений через точку в условии ПО не подходит по требованиям к оптимизации на конкретной СУБД, применим метод явного поуровневого сбора иерархии в пакете запросов. Этот способ гарантирует оптимальный план выполнения за счет использования индексов по родителю.
Разберем по шагам создание временных таблиц:
// Уровень 0: исходные позиции из документа
ВЫБРАТЬ
ДокТЧ.Номенклатура КАК ИсходныйТовар,
ДокТЧ.Номенклатура КАК Аналог,
0 КАК УровеньВложенности
ПОМЕСТИТЬ ВТ_Иерархия
ИЗ
ДокТЧ КАК ДокТЧ
ОБЪЕДИНИТЬ ВСЕ
// Уровень 1: прямые потомки
ВЫБРАТЬ
ДокТЧ.Номенклатура,
СпрНом1.Ссылка,
1
ИЗ
ДокТЧ КАК ДокТЧ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНом1
ПО ДокТЧ.Номенклатура = СпрНом1.Родитель
ОБЪЕДИНИТЬ ВСЕ
// Уровень 2: потомки второго поколения
ВЫБРАТЬ
ДокТЧ.Номенклатура,
СпрНом2.Ссылка,
2
ИЗ
ДокТЧ КАК ДокТЧ
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНом1
ПО ДокТЧ.Номенклатура = СпрНом1.Родитель
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНом2
ПО СпрНом1.Ссылка = СпрНом2.Родитель
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Иерархия.ИсходныйТовар КАК БазоваяНоменклатура,
ВТ_Иерархия.Аналог КАК Аналог,
ВТ_Иерархия.УровеньВложенности КАК Приоритет
ИЗ
ВТ_Иерархия КАК ВТ_Иерархия
УПОРЯДОЧИТЬ ПО
БазоваяНоменклатура,
Приоритет
В данном запросе мы на каждом шаге «протаскиваем» исходную номенклатуру документа сквозь уровни подчинения. В результате формируется плоская таблица соответствий, где каждый дочерний элемент четко знает своего родоначальника.
Если задача заключается в формировании отчета, а не в проведении документа, проще и правильнее отказаться от ручного разбора иерархии в тексте запроса и воспользоваться возможностями Системы компоновки данных:
Номенклатура (или остатки по регистру). В секцию ГДЕ этого набора добавим условие: ГДЕ Номенклатура В ИЕРАРХИИ (&РодительскаяНоменклатура).Номенклатура первого набора, приемником — параметр РодительскаяНоменклатура, а в колонке условия укажем В ИЕРАРХИИ.Платформа автоматически выполнит обход дерева для каждого элемента верхнего уровня, избавив разработчика от необходимости писать громоздкие соединения в тексте запроса.
Выясним причину, почему иерархия элементов редко используется для хранения аналогов на реальных проектах и при сдаче экзаменов. В соответствии с архитектурой 1С элемент справочника может иметь строго одного родителя. Однако в реальной торговле товар «Болт М6х20» может являться полноценным аналогом как для «Болт М6х22», так и для «Болт М6х18», то есть отношение носит характер «многие-ко-многим».
Методически верным решением задачи подбора аналогов является создание независимого непериодического регистра сведений (например, АналогиНоменклатуры) с двумя ключевыми измерениями: Номенклатура и Аналог. В этом случае запрос на получение остатков и самих взаимозаменяемых товаров превращается в элементарное левое соединение с регистром, не требующее никаких ухищрений с обходом иерархических структур.