Почему не работает условие ПустаяСсылка В ИЕРАРХИИ ПустаяСсылка в 1С и как правильно оптимизировать такие запросы

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

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

  1. Условие Ссылка В ИЕРАРХИИ (ПустаяСсылка) всегда возвращает значение ИСТИНА для любого существующего элемента.
  2. Условие ПустаяСсылка В ИЕРАРХИИ (ПустаяСсылка) внезапно возвращает значение ЛОЖЬ.

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

Физическая структура таблиц и модель списка смежности

Для того чтобы понять логику работы платформы 1С при вычислении иерархии, нам необходимо изучить, как иерархические данные хранятся на уровне СУБД. Платформа 1С использует классическую реляционную модель Adjacency List (список смежности).

В физической таблице справочника (например, контрагентов или номенклатуры) для каждого элемента выделено поле Родитель (на уровне СУБД это поле имеет имя _ParentIDRRef). В этом поле хранится уникальный идентификатор (GUID) родительской группы:

Проанализируем ситуацию, когда мы выполняем условие Ссылка В ИЕРАРХИИ (ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)) для любого реального элемента:

  1. СУБД начинает рекурсивно подниматься по цепочке родителей для выбранного элемента.
  2. Для элемента верхнего уровня СУБД считывает значение поля Родитель и видит там пустую ссылку.
  3. Поскольку конечной точкой любой ветки иерархии (корнем) является пустая ссылка, система делает логический вывод, что абсолютно все физические элементы справочника иерархически подчинены пустой ссылке. Условие возвращает ИСТИНА.

Теперь выясним причину, почему для самой пустой ссылки результат будет противоположным. Когда мы проверяем ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка) В ИЕРАРХИИ (ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)):

  1. Пустая ссылка — это лишь техническое обозначение отсутствия записи. Физически в таблице базы данных СУБД нет строки, у которой поле Ссылка (_IDRRef) заполнено нулями.
  2. Платформа 1С строит дерево иерархии исключительно на основе существующих записей в СУБД.
  3. СУБД пытается найти в таблице запись с идентификатором пустой ссылки, чтобы определить её родителя. Не найдя такой записи, система не может построить цепочку связей и возвращает ЛОЖЬ.

Что происходит «под капотом» СУБД и почему это «валит» сервер?

В современных СУБД (MS SQL Server, PostgreSQL) нет прямого оператора В ИЕРАРХИИ. Платформа 1С транслирует этот оператор через собственный внутренний язык запросов SDBL в сложную конструкцию СУБД. Обычно это рекурсивные обобщенные табличные выражения (Recursive CTE) или создание временных таблиц с итерационным циклом наполнения.

Когда мы передаем конкретную группу в качестве параметра (например, &ГруппаКонтрагентов), СУБД быстро находит эту группу и выбирает только её подчиненные элементы. Глубина поиска жестко ограничена рамками этой группы.

Но если мы передаем в иерархию Пустую ссылку, для СУБД это означает следующее:

  1. Необходимо построить дерево иерархии, начиная с корневого элемента, коим является пустая ссылка.
  2. Поскольку пустая ссылка — это общий родитель для ВСЕХ корневых элементов, СУБД начинает **полный обход всего справочника** сверху вниз.
  3. На стороне СУБД запускается тяжелейший рекурсивный процесс, который выбирает каждую запись справочника, проверяет её уровень и записывает во временную таблицу.
  4. В результате строится гигантская временная таблица, содержащая ссылки на абсолютно все элементы и группы данного справочника.

Если в вашем справочнике Номенклатура или Контрагенты содержатся десятки или сотни тысяч записей, такой запрос гарантированно приведет к следующим последствиям:

Именно поэтому среди профессиональных разработчиков 1С действует негласное, но жесткое правило: никогда не допускать выполнения оператора В ИЕРАРХИИ с пустой ссылкой!

Разбираем практические методы решения проблемы

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

Способ 1. Безопасное логическое условие в секции ГДЕ (Рекомендуемый для простых запросов)

Этот способ основан на особенностях работы оптимизаторов современных СУБД. Если в условии ИЛИ левая часть возвращает истину, то правая часть даже не начинает вычисляться. Посмотрим на пример кода запроса:


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

Разберем по шагам, как работает это условие:

  1. Если пользователь оставил отбор пустым, параметр &ПараметрГруппа равен пустой ссылке.
  2. Первая часть условия &ПараметрГруппа = ЗНАЧЕНИЕ(...) возвращает ИСТИНА.
  3. СУБД понимает, что общее условие уже выполнено, и полностью игнорирует вычисление тяжелой правой части Контрагенты.Ссылка В ИЕРАРХИИ (&ПараметрГруппа). Полного обхода дерева не происходит, запрос выполняется мгновенно!
  4. Если же пользователь выбрал конкретную группу, первая часть возвращает ЛОЖЬ, и СУБД переходит к вычислению отбора по иерархии только для выбранной группы.

Способ 2. Использование дополнительного флага-параметра

Этот способ аналогичен первому, но вместо сравнения с пустой ссылкой внутри запроса мы передаем готовый булев флаг, указывающий на то, заполнен ли отбор:


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

В коде инициализации запроса на встроенном языке мы пишем следующую логику:


Запрос = Новый Запрос;
Запрос.Текст = "..." // Текст запроса выше

ОтборПоставлен = ЗначениеЗаполнено(ПараметрГруппа);
Запрос.УстановитьПараметр("ОтборПоставлен", ОтборПоставлен);
Запрос.УстановитьПараметр("ВыбраннаяГруппа", ПараметрГруппа);

Результат = Запрос.Выполнить();

Если &ОтборПоставлен равен Ложь, левая часть условия НЕ &ОтборПоставлен превращается в ИСТИНА, и СУБД вновь игнорирует выполнение рекурсивного поиска по иерархии.

Способ 3. Динамическое конструирование текста запроса

Если мы пишем сложный алгоритм во встроенном языке, наилучшим решением с точки зрения производительности СУБД будет полное исключение условия из текста запроса, если параметр не заполнен. Рассмотрим пример рефакторинга кода из обсуждения на форуме:


// Подготавливаем базовый текст запроса
ТекстЗапроса = 
"ВЫБРАТЬ
|   Взаиморасчеты.Контрагент КАК Контрагент,
|   Взаиморасчеты.СуммаОстаток КАК Остаток
|ИЗ
|   РегистрНакопления.Взаиморасчеты.Остатки КАК Взаиморасчеты
|{ГДЕ_УСЛОВИЕ_ФИЛЬТРА}";

// Анализируем заполненность отбора
Если ЗначениеЗаполнено(ОтборГруппаКонтрагентов) Тогда
    Условие = "ГДЕ Взаиморасчеты.Контрагент В ИЕРАРХИИ(&ОтборГруппа)";
    Запрос.УстановитьПараметр("ОтборГруппа", ОтборГруппаКонтрагентов);
Иначе
    // Если отбор не задан, мы просто вырезаем условие из запроса
    Условие = "";
КонецЕсли;

ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "{ГДЕ_УСЛОВИЕ_ФИЛЬТРА}", Условие);
Запрос.Текст = ТекстЗапроса;

Этот способ является наиболее чистым с точки зрения формируемого SQL-кода, так как СУБД вообще не получает лишних логических ветвлений и выполняет максимально простой и эффективный план запроса.

Способ 4. Перенос отборов в Систему компоновки данных (СКД)

Если вы разрабатываете отчет, настоятельно рекомендуется отказаться от жесткого прописывания условий В ИЕРАРХИИ в тексте запроса. Вместо этого используйте возможности Системы компоновки данных (СКД):

  1. Оставьте текст запроса чистым, без секции ГДЕ для иерархических полей.
  2. На вкладке «Настройки» СКД добавьте отбор по полю Контрагент с типом сравнения В иерархии.
  3. СКД самостоятельно проанализирует действия пользователя. Если пользователь не заполнил отбор, СКД динамически уберет это условие из финального SQL-запроса. Если пользователь установит группу, СКД подставит эффективный отбор.

Резюме

Мы выяснили, что странное поведение условий с пустой ссылкой в иерархии — это не баг платформы 1С, а логическое следствие реляционной структуры хранения данных и модели списка смежности. Передача пустой ссылки в оператор В ИЕРАРХИИ заставляет СУБД строить дерево для всего справочника, что приводит к критическому падению производительности.

Используйте безопасное конструирование условий с оператором ИЛИ, динамически управляйте текстом запроса на встроенном языке или доверяйте построение отборов Системе компоновки данных. Эти простые правила позволят вашим базам данных работать быстро и стабильно даже на миллионных массивах информации.

← На главную