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

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

При разработке механизмов ценообразования, систем скидок или правил печати документов программисты 1С часто сталкиваются с необходимостью хранить настройки не для конкретных элементов справочника, а для целых групп. Например, в регистре сведений может быть измерение Контрагент, где записана группа «Оптовые покупатели». Когда мы пытаемся проверить в запросе, действует ли это правило для конкретного «Иванова», возникают сложности с синтаксисом.

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

Почему стандартный оператор «В ИЕРАРХИИ» выдает ошибку?

Рассмотрим ситуацию: у нас есть искомый клиент (параметр &ПарамКлиент) и таблица регистра, где в поле Контрагент может лежать либо сам клиент, либо его родительская группа. Интуитивно хочется написать так:


ГДЕ
    &ПарамКлиент В ИЕРАРХИИ (Регистр.Контрагент)

При попытке выполнить такой запрос платформа выдаст ошибку: «Неверные параметры "В ИЕРАРХИИ"». Выясним причину: в языке запросов 1С оператор В ИЕРАРХИИ работает строго в одном направлении. Он ожидает, что слева находится поле таблицы, а справа — конкретное значение или массив значений (параметр), для которых платформа должна построить дерево вниз. Платформа не умеет для каждой строки таблицы «на лету» строить дерево вверх, чтобы проверить, является ли значение в колонке родителем нашего параметра.

Рассмотрим рабочие способы обхода этого ограничения.

Способ 1: Предварительный сбор родителей в массив (рекомендуемый)

Проанализируем самый надежный и производительный метод. Вместо того чтобы заставлять запрос искать иерархию, мы передадим ему уже готовый список всех «покровителей» нашего элемента. Если «Иванов» входит в группу «Мелкий опт», которая входит в группу «Оптовики», то массив будет содержать: [Иванов, Мелкий опт, Оптовики].

Для получения этого списка можно использовать простой цикл, так как глубина иерархии в справочниках редко превышает 5–10 уровней. Разберем пример кода:


// Получаем всех родителей элемента
МассивРодителей = Новый Массив;
ТекущаяСсылка = ПараметрОбъект; // Наш элемент (например, Контрагент)

Пока ЗначениеЗаполнено(ТекущаяСсылка) Цикл
    МассивРодителей.Добавить(ТекущаяСсылка);
    ТекущаяСсылка = ТекущаяСсылка.Родитель;
КонецЦикла;

// Теперь передаем этот массив в запрос
Запрос.УстановитьПараметр("МассивРодителей", МассивРодителей);

Теперь текст самого запроса становится максимально простым и эффективным:


ВЫБРАТЬ
    Правила.ВидДокумента,
    Правила.Контрагент
ИЗ
    РегистрСведений.лк_ПравилаПечатиМаркЛиста КАК Правила
ГДЕ
    Правила.Контрагент В (&МассивРодителей)

Этот метод работает быстрее всего, так как использует обычный индекс по измерению Контрагент и простое сравнение IN в SQL.

Способ 2: Использование иерархических итогов

Иногда требуется получить список родителей прямо в коде запроса, не обращаясь к объектной модели. Для этого можно использовать конструкцию ИТОГИ ПО ССЫЛКА ТОЛЬКО ИЕРАРХИЯ. Посмотрим на этот прием подробнее:


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

Важное ограничение: результат такого запроса нельзя поместить во временную таблицу (ВТ), так как ИТОГИ в ВТ не поддерживаются. Вам придется сначала выполнить этот запрос, выгрузить результат в массив в коде 1С, а затем передать этот массив во второй запрос. Это делает данный способ менее удобным, чем «Способ 1».

Способ 3: Оптимизированный выбор приоритетного правила

Проанализируем ситуацию, когда для одного клиента может найтись сразу несколько правил: одно задано для него лично, другое — для его группы, третье — для всех контрагентов (корень справочника). Какое правило выбрать?

Логично предположить, что наиболее специфичное правило (установленное для самого элемента) важнее общего правила для группы. Чтобы реализовать это в запросе, мы можем добавить поле Уровень. Посмотрим, как это сделать:

  1. Сформируем массив родителей (как в первом способе).
  2. В запросе соединимся со справочником, чтобы получить уровень иерархии каждого родителя.
  3. Отсортируем результат по уровню в убывающем порядке и выберем первую запись.

ВЫБРАТЬ ПЕРВЫЕ 1
    Правила.ВидДокумента,
    Правила.Контрагент,
    Правила.Контрагент.Уровень КАК Уровень
ИЗ
    РегистрСведений.лк_ПравилаПечатиМаркЛиста КАК Правила
ГДЕ
    Правила.Контрагент В (&МассивРодителей)
УПОРЯДОЧИТЬ ПО
    Уровень УБЫВ

Таким образом, если в регистре есть запись и для группы (уровень 2), и для элемента (уровень 5), система корректно выберет запись для элемента.

Альтернативные методы: Полный код и вспомогательные таблицы

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

Использование «Полного кода»: Если в справочнике настроена уникальность кодов и иерархическое представление (строка вида «001/005/012»), можно использовать текстовое сравнение. Рассмотрим логику: если полный код нашего элемента 0001/0045/0098, то любой его родитель будет иметь код, являющийся началом этой строки. В запросе это можно проверить через ПОДОБНО, но это требует хранения «Полного пути» в отдельном реквизите и обычно считается «костылем».

Матрица входимости: Создается вспомогательный регистр сведений, где хранятся пары Элемент — Предок для всех уровней. Это позволяет делать связь (Join) в запросе напрямую, без использования массивов и иерархических операторов. Это самый мощный метод для сложных аналитических отчетов, но он требует поддержания актуальности данных в этой матрице при каждом переносе элементов в справочнике.

Подведем итоги

Для большинства задач в 1С оптимальным является Способ №1. Он прост в реализации, не создает нагрузки на сервер базы данных и легко читается другими разработчиками. Достаточно собрать родителей элемента в массив через свойство .Родитель в цикле и передать этот массив в параметр запроса. Это гарантирует корректную работу условия вхождения в группу вне зависимости от ограничений языка запросов.

← На главную