При работе с иерархическими справочниками в 1С:Предприятие начинающие (а иногда и опытные) разработчики сталкиваются с неочевидным поведением запросов при обращении к родителям через «точку». Ситуация, когда одно поле возвращает пустую строку, а другое — NULL, может вызвать замешательство. В этой статье мы подробно разберем механику работы системы, выясним причины такого поведения и рассмотрим профессиональные подходы к решению подобных задач.
Рассмотрим исходный запрос, в котором автор пытается получить наименования родителей разной вложенности. Проблема заключается в том, что в системе 1С обращение через точку в языке запросов — это не просто переход к значению, а полноценное левое соединение (LEFT JOIN) на уровне СУБД. Проанализируем, что происходит при выполнении следующей цепочки:
Номенклатура.Родитель.Родитель.Родитель...Наименование
Когда запрос доходит до элемента, у которого нет родителя (верхний уровень иерархии), поле Родитель содержит пустую ссылку. В базе данных это значение представлено уникальным идентификатором, состоящим из нулей. Однако в физической таблице справочника записи с таким идентификатором не существует. Поэтому, когда СУБД пытается выполнить LEFT JOIN для получения следующего уровня или реквизита (например, Наименование), соединение не находит соответствующую строку в правой таблице. В результате все поля этой «несуществующей» строки принимают значение NULL.
В примере автора Поле1 вернуло пустую строку "", а Поле2 вернуло NULL. Давайте выясним причину, проанализировав логику оператора ВЫБОР:
Номенклатура.Родитель...Родитель ЕСТЬ НЕ NULL. Поскольку на этом уровне иерархии ссылка еще существовала (пусть и была пустой), условие КОГДА сработало. Но в секции ТОГДА произошло обращение к Наименованию. Как мы уже выяснили, получение реквизита от несуществующей в таблице записи (пустой ссылки) дает NULL. Следовательно, результат всего поля стал NULL.ЕСТЬ НЕ NULL выполнялась для еще более глубокого уровня. На этом этапе цепочка соединений SQL уже «оборвалась» раньше, и сама ссылка на родителя родителя стала NULL с точки зрения SQL-выборки. Условие КОГДА не выполнилось, и сработала секция ИНАЧЕ, которая вернула заданную автором пустую строку "".Проанализируем ситуацию с точки зрения эффективности. Каждое обращение через точку — это дополнительная нагрузка на сервер базы данных. Если мы пишем Родитель.Родитель.Родитель, мы заставляем SQL-сервер соединять таблицу саму с собой трижды. В случае с 6-7 уровнями, как в примере, количество соединений становится критическим.
Основные риски такого подхода:
Как отмечают эксперты в обсуждении, для высоконагруженных систем или больших справочников (например, когда в справочнике Номенклатура миллионы записей) используется прием, называемый денормализацией. Рассмотрим его подробнее.
Суть метода заключается в хранении «полного пути» или «иерархического индекса» непосредственно в карточке элемента. Мы можем создать дополнительный реквизит, например, ПолныйПутьНаименований с типом Строка. При записи элемента или группы мы программно вычисляем всю цепочку родителей и записываем её в этот реквизит.
Пример кода при записи:
Процедура ПередЗаписью(Отказ)
Если ЭтоГруппа Тогда
// Рекурсивно или циклом собираем имена родителей
ТекущийРодитель = Родитель;
Путь = "";
Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
Путь = ТекущийРодитель.Наименование + " / " + Путь;
ТекущийРодитель = ТекущийРодитель.Родитель;
КонецЦикла;
ПутьКГруппе = Путь;
КонецЕсли;
КонецПроцедуры
Теперь в запросе нам не нужно строить цепочки из Родитель.Родитель.... Нам достаточно выбрать одно поле ПутьКГруппе, что будет работать мгновенно независимо от глубины вложенности.
Если задача заключается в выводе отчета с иерархией, разберем более правильный встроенный механизм — использование СКД. Система компоновки данных умеет работать с иерархией нативно. Нам не нужно вручную прописывать уровни. Достаточно в настройках структуры отчета указать тип вывода «Иерархия». Платформа сама выполнит необходимые рекурсивные запросы, которые оптимизированы на уровне ядра.
Для фильтрации данных по родителям всегда следует использовать оператор В ИЕРАРХИИ. Проанализируем, почему это лучше ручного перебора:
ГДЕ
Номенклатура.Ссылка В ИЕРАРХИИ(&НужнаяГруппа)
Этот оператор заставляет платформу использовать внутренние механизмы поиска всех потомков указанного узла. В зависимости от СУБД, 1С может использовать специальные временные таблицы или оптимизированные индексы по полю _ParentIDRRef, что значительно быстрее, чем множественные JOIN.
Проанализировав ситуацию, мы можем сформулировать следующие правила работы с иерархией в запросах 1С:
Наименование от пустой ссылки — это NULL, а сама пустая ссылка — это НЕ NULL (в контексте значения поля ссылки в первой таблице).Следуя этим рекомендациям, вы сможете создавать надежные и быстрые приложения, которые не будут «тормозить» при росте объема данных или изменении структуры справочников пользователями.