Почему запрос к иерархическому справочнику через несколько «точек» возвращает неверный результат и как это исправить?

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

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

Анализ проблемы: почему «лыжи не едут»

Рассмотрим ситуацию, когда разработчик пытается выбрать элементы, которые НЕ входят в определенный список групп. Вместо использования оператора В ИЕРАРХИИ, в запросе прописывается цепочка условий:


ВЫБРАТЬ
    *
ИЗ
    ТаблицаДанных КАК ТаблицаДанных
ГДЕ
    ТаблицаДанных.Номенклатура.ЭтоГруппа ИЛИ
    НЕ
    (ТаблицаДанных.Номенклатура.Родитель В (&МассивГрупп) ИЛИ
    ТаблицаДанных.Номенклатура.Родитель.Родитель В (&МассивГрупп) ИЛИ
    ТаблицаДанных.Номенклатура.Родитель.Родитель.Родитель В (&МассивГрупп) ИЛИ
    ТаблицаДанных.Номенклатура.Родитель.Родитель.Родитель.Родитель В (&МассивГрупп) ИЛИ
    ТаблицаДанных.Номенклатура.Родитель.Родитель.Родитель.Родитель.Родитель В (&МассивГрупп))

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

1. Коварство значения NULL

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

Если мы запрашиваем уровень родителя, которого физически не существует для данного элемента (например, элемент находится на 2-м уровне, а мы просим 5-го родителя), СУБД возвращает значение NULL. Разберем, как это влияет на логику условия:

  1. Проверка NULL В (&МассивГрупп) всегда возвращает результат Ложь (так как NULL не равен ни одному значению в массиве ссылок).
  2. Если все уровни проверки вернули Ложь, то всё выражение внутри скобок становится Ложь.
  3. Оператор НЕ (Ложь) превращает результат в Истину.

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

2. Пустая ссылка vs NULL

Важно понимать различие между этими понятиями в контексте иерархии:

Пустая ссылка — это реальное значение в поле Родитель у элементов, которые находятся в корне справочника. Если в вашем &МассивГрупп нет пустой ссылки, то проверка Родитель В (&МассивГрупп) для корневых элементов вернет Ложь.

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

3. Производительность и неявные соединения

Проанализируем ситуацию с точки зрения нагрузки на систему. Каждая точка в запросе — это отдельное соединение таблиц. Если вы написали цепочку из 6 родителей, вы заставили SQL-сервер соединить таблицу номенклатуры саму с собой 6 раз! Рассмотрим последствия:

  1. Раздувание плана запроса: SQL-серверу становится сложно оптимизировать такой запрос.
  2. Лимиты СУБД: Существует ограничение на количество таблиц в одном запросе. Глубокое разыменовывание может привести к ошибке исполнения.
  3. Чтение лишних данных: Даже если вам нужна только ссылка, СУБД приходится считывать данные всех промежуточных узлов.

Как сделать правильно: использование оператора В ИЕРАРХИИ

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


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

Почему это лучше:

Дополнительные рекомендации по оптимизации

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

Избегайте «ВЫБРАТЬ *» во временных таблицах

Посмотрим на пример из темы: ВЫБРАТЬ * ПОМЕСТИТЬ ТаблицаДанных. Использование «звездочки» считается плохой практикой, особенно при помещении данных во временную таблицу. Это заставляет систему копировать все поля объекта, включая тяжелые реквизиты, которые могут не понадобиться в дальнейшем. Всегда перечисляйте только те поля, которые действительно нужны для дальнейшей работы.

Осторожно с «УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ»

Конструкция УПОРЯДОЧИТЬ ПО Номенклатура ИЕРАРХИЯ — это мощный инструмент для визуализации дерева, но он крайне ресурсозатратен. Платформа выполняет дополнительные обращения к базе данных, чтобы восстановить структуру дерева для каждой строки результата. Если вам нужно просто отфильтровать список для программной обработки, используйте обычную сортировку по наименованию или коду.

Обработка пустых значений в массивах

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

Резюме

Подведем итог нашему анализу. Попытка «ручного» обхода иерархии через точки — это классический пример «антипаттерна» в разработке на 1С. Это приводит к:

Используйте встроенный функционал платформы — оператор В ИЕРАРХИИ. Это гарантирует корректность результата и стабильную работу вашей программы.

← На главную