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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

При попытке связать строки табличной части документа со всеми подчиненными элементами иерархии через один запрос разработчики часто сталкиваются с тем, что вместо красивой группировки товары выводятся общим неструктурированным списком, а в колонках базовой номенклатуры появляются пустые значения (NULL). Разберем причины такого поведения платформы и подробно рассмотрим работающие способы решения задачи.

Анализ типовых ошибок в запросе

Рассмотрим исходный запрос, в котором была сделана попытка связать номенклатуру документа с подчиненными элементами:


ВЫБРАТЬ
    ДокТЧ.Номенклатура КАК Номенклатура,
    Ном.Ссылка КАК Аналог
ИЗ
    ДокТЧ КАК ДокТЧ
    ПОЛНОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Ном
    ПО ДокТЧ.Номенклатура = Ном.Ссылка
ГДЕ
    Ном.Ссылка В ИЕРАРХИИ
        (ВЫБРАТЬ
            ДокТч.Номенклатура
        ИЗ
            ДокТч)

Проанализируем две ключевые логические ошибки, допущенные в этом коде:

  1. Ошибочное условие соединения. Конструкция ПО ДокТЧ.Номенклатура = Ном.Ссылка пытается связать строку документа только с точно таким же элементом справочника. Для дочерних элементов (собственно аналогов) ссылка не совпадает с ссылкой родителя из табличной части документа. Из-за использования ПОЛНОЕ СОЕДИНЕНИЕ (или ЛЕВОЕ СОЕДИНЕНИЕ) платформа присоединяет подчиненные строки без сопоставления с левой таблицей, заполняя поле ДокТЧ.Номенклатура значением NULL.
  2. Условие фильтрации вместо связи. Конструкция ГДЕ Ном.Ссылка В ИЕРАРХИИ (...) лишь отсекает лишние элементы справочника, которые не входят ни в одну из веток указанных в документе товаров. Однако этот фильтр никак не указывает СУБД, к какой именно строке документа относится конкретный найденный подчиненный элемент. В результате выборка аналогов смешивается в единый список.

Главное ограничение платформы 1С заключается в том, что оператор В ИЕРАРХИИ категорически запрещено использовать внутри предложения ПО при соединении таблиц. Язык запросов 1С разрешает применять его только в блоках ГДЕ и ИМЕЮЩИЕ. Поэтому напрямую соединить таблицы по условию вхождения в ветку невозможно. Рассмотрим практические пути обхода этого ограничения.

Способ 1. Соединение по цепочке реквизитов родителя

Если глубина вложенности иерархии элементов заранее известна и ограничена (например, до трех или четырех уровней), наиболее быстрым и эффективным приемом является соединение по реквизиту Родитель с проверкой всех возможных уровней иерархии.

Разберем готовый пример запроса:


ВЫБРАТЬ
    ДокТЧ.Номенклатура КАК БазоваяНоменклатура,
    Аналоги.Ссылка КАК НоменклатураАналог,
    ВЫБОР
        КОГДА Аналоги.Ссылка = ДокТЧ.Номенклатура
            ТОГДА 1
        ИНАЧЕ 2
    КОНЕЦ КАК Приоритет
ИЗ
    ДокТЧ КАК ДокТЧ
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Аналоги
        ПО ДокТЧ.Номенклатура = Аналоги.Ссылка
            ИЛИ ДокТЧ.Номенклатура = Аналоги.Родитель
            ИЛИ ДокТЧ.Номенклатура = Аналоги.Родитель.Родитель
            ИЛИ ДокТЧ.Номенклатура = Аналоги.Родитель.Родитель.Родитель
УПОРЯДОЧИТЬ ПО
    БазоваяНоменклатура,
    Приоритет,
    НоменклатураАналог
ИТОГИ ПО
    БазоваяНоменклатура

Посмотрим на логику работы этого решения:

  1. Используется ЛЕВОЕ СОЕДИНЕНИЕ от таблицы документа. Это гарантирует, что даже если у позиции нет подчиненных аналогов, сам исходный товар останется в выборке.
  2. В секции ПО задается составное условие через оператор ИЛИ. Строка документа связывается как с самой собой (для получения остатков исходной позиции с наивысшим приоритетом), так и со своими потомками первого, второго и третьего уровней.
  3. Благодаря явному сохранению связи в поле ДокТЧ.Номенклатура каждый найденный аналог жестко привязан к конкретному товару из документа. Секция ИТОГИ ПО или последующая группировка выборки в коде 1С позволяет корректно обойти аналоги раздельно по каждой исходной строке.

Способ 2. Поуровневый разворот дерева через временные таблицы

Если цепочка обращений через точку в условии ПО не подходит по требованиям к оптимизации на конкретной СУБД, применим метод явного поуровневого сбора иерархии в пакете запросов. Этот способ гарантирует оптимальный план выполнения за счет использования индексов по родителю.

Разберем по шагам создание временных таблиц:


// Уровень 0: исходные позиции из документа
ВЫБРАТЬ
    ДокТЧ.Номенклатура КАК ИсходныйТовар,
    ДокТЧ.Номенклатура КАК Аналог,
    0 КАК УровеньВложенности
ПОМЕСТИТЬ ВТ_Иерархия
ИЗ
    ДокТЧ КАК ДокТЧ

ОБЪЕДИНИТЬ ВСЕ

// Уровень 1: прямые потомки
ВЫБРАТЬ
    ДокТЧ.Номенклатура,
    СпрНом1.Ссылка,
    1
ИЗ
    ДокТЧ КАК ДокТЧ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНом1
        ПО ДокТЧ.Номенклатура = СпрНом1.Родитель

ОБЪЕДИНИТЬ ВСЕ

// Уровень 2: потомки второго поколения
ВЫБРАТЬ
    ДокТЧ.Номенклатура,
    СпрНом2.Ссылка,
    2
ИЗ
    ДокТЧ КАК ДокТЧ
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНом1
        ПО ДокТЧ.Номенклатура = СпрНом1.Родитель
        ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК СпрНом2
        ПО СпрНом1.Ссылка = СпрНом2.Родитель
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Иерархия.ИсходныйТовар КАК БазоваяНоменклатура,
    ВТ_Иерархия.Аналог КАК Аналог,
    ВТ_Иерархия.УровеньВложенности КАК Приоритет
ИЗ
    ВТ_Иерархия КАК ВТ_Иерархия
УПОРЯДОЧИТЬ ПО
    БазоваяНоменклатура,
    Приоритет

В данном запросе мы на каждом шаге «протаскиваем» исходную номенклатуру документа сквозь уровни подчинения. В результате формируется плоская таблица соответствий, где каждый дочерний элемент четко знает своего родоначальника.

Способ 3. Связи наборов данных в СКД (для отчетов)

Если задача заключается в формировании отчета, а не в проведении документа, проще и правильнее отказаться от ручного разбора иерархии в тексте запроса и воспользоваться возможностями Системы компоновки данных:

  1. Создадим Набор данных 1 с простым запросом по строкам документа: получаем только перечень позиций.
  2. Создадим Набор данных 2, выбирающий данные справочника Номенклатура (или остатки по регистру). В секцию ГДЕ этого набора добавим условие: ГДЕ Номенклатура В ИЕРАРХИИ (&РодительскаяНоменклатура).
  3. На вкладке «Связи наборов данных» свяжем первый набор со вторым: источником связи выступает Номенклатура первого набора, приемником — параметр РодительскаяНоменклатура, а в колонке условия укажем В ИЕРАРХИИ.

Платформа автоматически выполнит обход дерева для каждого элемента верхнего уровня, избавив разработчика от необходимости писать громоздкие соединения в тексте запроса.

Архитектурная рекомендация для практических задач

Выясним причину, почему иерархия элементов редко используется для хранения аналогов на реальных проектах и при сдаче экзаменов. В соответствии с архитектурой 1С элемент справочника может иметь строго одного родителя. Однако в реальной торговле товар «Болт М6х20» может являться полноценным аналогом как для «Болт М6х22», так и для «Болт М6х18», то есть отношение носит характер «многие-ко-многим».

Методически верным решением задачи подбора аналогов является создание независимого непериодического регистра сведений (например, АналогиНоменклатуры) с двумя ключевыми измерениями: Номенклатура и Аналог. В этом случае запрос на получение остатков и самих взаимозаменяемых товаров превращается в элементарное левое соединение с регистром, не требующее никаких ухищрений с обходом иерархических структур.

← На главную