При разработке запросов к иерархическим справочникам часто возникает дилемма: использовать строгое равенство по реквизиту Родитель или универсальное условие В ИЕРАРХИИ. На небольших базах разница во времени выполнения незаметна, однако на объемах в сотни тысяч и миллионы записей выбор конструкции напрямую влияет на стабильность всей системы. Разберем по шагам, как работают эти конструкции на уровне СУБД, какие риски несет рекурсивный поиск и как правильно оптимизировать иерархические выборки.
Чтобы понять разницу в быстродействии, проанализируем поведение оптимизатора СУБД при выполнении каждого из вариантов.
WHERE _ParentIDRRef = @P. Поле родителя входит в состав стандартных индексов таблицы справочника. СУБД выполняет точечный поиск по индексу (Index Seek), что занимает доли миллисекунды независимо от общего размера таблицы (будь то 10 тысяч или 5 миллионов строк).В СПИСКЕ. Чем больше глубина вложенности и количество подчиненных групп, тем больше ресурсов требуется для обхода дерева. В таких СУБД, как PostgreSQL, рекурсивные CTE часто материализуются в памяти или во временных таблицах на диске, вызывая дополнительную дисковую нагрузку.Выясним причину, почему универсальное условие В ИЕРАРХИИ может стать узким местом производительности:
Номенклатура В ИЕРАРХИИ (&Группа) в параметры виртуальной таблицы регистра (например, остатков или оборотов) является грубой ошибкой проектирования. Оптимизатор СУБД не может эффективно связать составной индекс итогов регистра с динамически вычисляемым деревом и переходит к полному сканированию всей таблицы итогов (Index Scan / Clustered Index Scan).Рассмотрим практические методы, позволяющие сохранить гибкость иерархии и при этом обеспечить максимальную скорость выполнения запросов.
Если требуется получить данные из регистра по иерархическому списку элементов, разделим операцию на два шага: сначала выберем все элементы группы во временную таблицу и проиндексируем ее, а затем свяжем с регистром.
Посмотрим на пример правильного построения пакетного запроса:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Номенклатура
ПОМЕСТИТЬ ВТ_ЭлементыИерархии
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.Ссылка В ИЕРАРХИИ(&ГруппаНоменклатуры)
И НЕ Номенклатура.ЭтоГруппа
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
Остатки.Номенклатура КАК Номенклатура,
Остатки.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(
&Период,
Номенклатура В
(ВЫБРАТЬ
ВТ_ЭлементыИерархии.Номенклатура
ИЗ
ВТ_ЭлементыИерархии)) КАК Остатки
Такой подход гарантирует, что в виртуальную таблицу регистра будет передан фиксированный список ссылок, и СУБД задействует штатный индекс таблицы итогов.
Если иерархия справочника стабильна, а выборки по крупным веткам каталога выполняются часто и критичны к скорости, применим денормализацию:
ГруппаВерхнегоУровня (или КореньИерархии) с признаком Индексировать.
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
Номенклатура.Артикул КАК Артикул
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
Номенклатура.ГруппаВерхнегоУровня = &НужнаяГруппа
И НЕ Номенклатура.ЭтоГруппа
Этот метод полностью устраняет накладные расходы на обход дерева, сохраняя быстродействие на уровне прямого равенства даже при миллионных объемах данных.
Если бизнес-процесс компании строго определяет, что элементы справочника располагаются исключительно на первом уровне вложенности выбранной папки и создание подпапок запрещено правами доступа, смело используем прямое условие Родитель = &НужнаяГруппа. Это избавит СУБД от построения планов с рекурсией и обеспечит минимальное время отклика.