Почему в запросе 1С при обращении к родителю возвращается NULL и как правильно работать с иерархией

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

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

Разбор ситуации: механизм работы «точки» в запросе

Рассмотрим исходный запрос, в котором автор пытается получить наименования родителей разной вложенности. Проблема заключается в том, что в системе 1С обращение через точку в языке запросов — это не просто переход к значению, а полноценное левое соединение (LEFT JOIN) на уровне СУБД. Проанализируем, что происходит при выполнении следующей цепочки:

Номенклатура.Родитель.Родитель.Родитель...Наименование

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

Почему результаты полей различаются?

В примере автора Поле1 вернуло пустую строку "", а Поле2 вернуло NULL. Давайте выясним причину, проанализировав логику оператора ВЫБОР:

  1. Для Поля 2: В условии проверялось Номенклатура.Родитель...Родитель ЕСТЬ НЕ NULL. Поскольку на этом уровне иерархии ссылка еще существовала (пусть и была пустой), условие КОГДА сработало. Но в секции ТОГДА произошло обращение к Наименованию. Как мы уже выяснили, получение реквизита от несуществующей в таблице записи (пустой ссылки) дает NULL. Следовательно, результат всего поля стал NULL.
  2. Для Поля 1: Здесь проверка ЕСТЬ НЕ NULL выполнялась для еще более глубокого уровня. На этом этапе цепочка соединений SQL уже «оборвалась» раньше, и сама ссылка на родителя родителя стала NULL с точки зрения SQL-выборки. Условие КОГДА не выполнилось, и сработала секция ИНАЧЕ, которая вернула заданную автором пустую строку "".

Проблема производительности: «Implicit Joins»

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

Основные риски такого подхода:

Метод денормализации данных

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

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

Пример кода при записи:


Процедура ПередЗаписью(Отказ)
    Если ЭтоГруппа Тогда
        // Рекурсивно или циклом собираем имена родителей
        ТекущийРодитель = Родитель;
        Путь = "";
        Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
            Путь = ТекущийРодитель.Наименование + " / " + Путь;
            ТекущийРодитель = ТекущийРодитель.Родитель;
        КонецЦикла;
        ПутьКГруппе = Путь;
    КонецЕсли;
КонецПроцедуры

Теперь в запросе нам не нужно строить цепочки из Родитель.Родитель.... Нам достаточно выбрать одно поле ПутьКГруппе, что будет работать мгновенно независимо от глубины вложенности.

Использование Системы компоновки данных (СКД)

Если задача заключается в выводе отчета с иерархией, разберем более правильный встроенный механизм — использование СКД. Система компоновки данных умеет работать с иерархией нативно. Нам не нужно вручную прописывать уровни. Достаточно в настройках структуры отчета указать тип вывода «Иерархия». Платформа сама выполнит необходимые рекурсивные запросы, которые оптимизированы на уровне ядра.

Работа с оператором В ИЕРАРХИИ

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


ГДЕ
    Номенклатура.Ссылка В ИЕРАРХИИ(&НужнаяГруппа)

Этот оператор заставляет платформу использовать внутренние механизмы поиска всех потомков указанного узла. В зависимости от СУБД, 1С может использовать специальные временные таблицы или оптимизированные индексы по полю _ParentIDRRef, что значительно быстрее, чем множественные JOIN.

Выводы и рекомендации

Проанализировав ситуацию, мы можем сформулировать следующие правила работы с иерархией в запросах 1С:

  1. Избегайте длинных цепочек через точку: Максимально допустимая вложенность «через точку» — 2-3 уровня. Если требуется больше, пересмотрите архитектуру решения.
  2. Проверяйте на NULL правильно: Помните, что Наименование от пустой ссылки — это NULL, а сама пустая ссылка — это НЕ NULL (в контексте значения поля ссылки в первой таблице).
  3. Используйте временные таблицы: Если нужно получить данные по родителям для списка элементов, лучше выгрузить родителей во временную таблицу и соединять их последовательно. Это позволит СУБД лучше оптимизировать план.
  4. Рассмотрите денормализацию: Если чтение иерархии происходит часто, а структура справочника меняется редко, хранение подготовленного пути в отдельном реквизите — лучшее решение для производительности.
  5. Доверяйте СКД: Для сложных отчетов с динамической глубиной иерархии стандартные механизмы компоновки данных всегда предпочтительнее «самописных» запросов с жестко зашитыми уровнями.

Следуя этим рекомендациям, вы сможете создавать надежные и быстрые приложения, которые не будут «тормозить» при росте объема данных или изменении структуры справочников пользователями.

← На главную