Повышение производительности при выборке по иерархии
Повышение производительности при выборке по иерархии
Структура хранения иерархических объектов 1С:Предприятия довольно проста. Каждая запись в таблице такого объекта имеет служебное поле, в котором хранится ссылка на родительский элемент. Это удобно для отображения списка справочника, когда отображаются элементы текущей (открытой в данный момент) группы. Однако выборка по иерархии подразумевает рекурсию выборок.
Когда в запросе используется конструкция «ГДЕ ХХХ.Ссылка В ИЕРАРХИИ (&YYY)», платформа сначала формирует список таких элементов по алгоритму: 1) создать пустой список элементов к последующей обработке; 2) добавить в него Параметр, переданный в запрос и установить на него указатель списка; 3) выбрать элементы, где Родитель=&(значение в указателе списка) и ЭтоГруппа; 4) добавить выбранные элементы в конец списка к последующей обработке; 5) сдвинуть указатель на следующую позицию; 6) если удалось (не достигнут конец списка), перейти к (3). Полученный список и используется как параметр запроса, а конструкция «ГДЕ ХХХ.Ссылка В ИЕРАРХИИ (&YYY)» заменяется на «ГДЕ ХХХ.Родитель В (&Список)»
Механизм довольно понятный и простой, однако малоэффективный. При достаточно большом ветвлении дерева данный подготовительный процесс занимает продолжительное время.
Например: в компаниях, торгующих запасными частями к автомобилям, каталоги товаров имеют достаточно большое ветвление по производителям, по узлам и агрегатам, и т.д. Для справочника «Номенклатура», в котором 80 000 элементов из которых 800 - группы, такой процесс может занимать до 3 секунд на нормальном, современном сервере. Но и основной запрос, в котором используется конструкция «В (&Список)» требует перебора всех записей таблицы и не оптимизируется сервером баз данных.
Введя небольшое функциональное ограничение, можно в корне изменить ситуацию. Основная цель состоит в том, чтобы одним запросом выбирать все ниже лежащие элементы. Для этого нужно ввести составной ключ, который будет содержать сумму кодов всех выше стоящих элементов. Пример: элемент имеет код 156 и находится на 3-м уровне справочника, код его родителя 112, код родителя его родителя 89 – в поле ключ должно быть записано «089112156». В таком случае все элементы, принадлежащие группе с кодом 89, будут иметь «089*» в ключевом поле. Элементы, принадлежащие группе с кодом 112, будут иметь «089112*» в ключевом поле. Этот подход позволяет заменить условие запроса «ГДЕ ХХХ.Ссылка В ИЕРАРХИИ (&ВыбГруппа)» на «ГДЕ ХХХ._Код ПОДОБНО "ВыбГруппа._Код%"» или «ПОДСТРОКА (ХХХ._Код, 0, " + СтрДлина(ВыбГруппа._Код) + ") = &ВыбГруппа._Код» для выбора всех элементов, принадлежащих группе «ВыбГруппа».
Рассмотрим реализацию данного подхода. Если мы предположим, что в одной группе не может находиться более N элементов, тогда для уникального определения элемента внутри группы достаточно Цел(Log10(N))+1 десятичных разрядов. Для общности мы будем полагать N=99999 (что редко достижимо на практике), в этом случае число необходимых разрядов 5. Теперь добавим в таблицу дополнительный индексируемый реквизит, например «_Код», строкового типа с длиной, достаточной для хранения нашей глубины иерархии. Если ожидается максимально 10 вложенностей, длина поля должна быть (10+1)*5=55. В модуле объекта справочника создадим процедуру ПередЗаписью, в которой сформируем новое значение ключа: _Код=СокрЛП(Родитель._Код)+Формат(Код,"ЧЦ="+РазрядностьКода+"; ЧВН=; ЧГ=0"). В форматной строке мы указываем, что нужно выводить лидирующие нули, дополнять длину строки нулями до разрядности кода и отключаем поразрядную группировку. Особое внимание нужно уделить изменению кода группы и переносу группы в другую группу. В этих случаях нужно обработать и все подчиненные элементы.
Дополнительный эффект от реализации этого метода: при необходимости найти родителя определенного элемента, находящегося на заданном уровне, нет необходимости итеративно получать ХХХ.Родитель и сравнивать его уровень с заданным. Достаточно взять (Уровень*5) левых символов и найти элемент с таким значением ключа.
Описанный механизм реализован в демонстрационной конфигурации, где можно сравнить производительность описанного метода со стандартным.
Если Вы можете дополнить этот материал или обнаружили ошибку, пожалуйста укажите это в комментариях или пишите автору.