Работа с большими объемами данных в иерархических структурах — одна из классических задач программиста 1С. Когда количество строк достигает десятков тысяч (например, 60 000 позиций номенклатуры), стандартные методы ручного перебора коллекций начинают демонстрировать крайне низкую производительность. В этой статье мы разберем, почему «ручной» обход в цикле работает медленно, и рассмотрим наиболее эффективные способы заполнения ДеревоЗначений, включая штатные механизмы платформы и архитектурные хитрости.
Рассмотрим ситуацию, с которой столкнулся автор темы. Попытка имитировать иерархию через фиксированное количество уровней в запросе (через Родитель.Родитель...) и последующий обход ТаблицаЗначений с проверкой изменения родителя — это путь к созданию избыточного и трудноподдерживаемого кода. Основные причины низкой скорости здесь:
Номенклатура.Родитель.Родитель заставляют СУБД выполнять неявные соединения таблиц, что замедляет получение данных.Добавить() для каждой строки дерева создают огромную нагрузку на интерпретатор 1С.Проанализируем более эффективные альтернативы.
Самый быстрый и правильный с точки зрения платформы способ — позволить СУБД и механизмам 1С самим построить дерево. Для этого в тексте запроса необходимо использовать предложение ИТОГИ ПО ... ИЕРАРХИЯ. Платформа 1С оптимизирована для того, чтобы нативно (на уровне C++) преобразовывать результат такого запроса в иерархическую коллекцию.
Рассмотрим пример реализации:
// 1. Формируем запрос с иерархическими итогами
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Номенклатура.Код КАК Код,
| Номенклатура.Артикул КАК Артикул
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ЛОЖЬ
|ИТОГИ ПО
| Номенклатура ИЕРАРХИЯ";
РезультатЗапроса = Запрос.Выполнить();
// 2. Выгружаем напрямую в дерево значений
// Параметр ОбходРезультатаЗапроса.ПоГруппировкамСИерархией критически важен
ДеревоРезультат = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией);
Этот метод работает в десятки раз быстрее ручного обхода, так как Выгрузить() выполняет заполнение структуры данных единым блоком памяти. Если данных очень много, именно этот способ является базовым.
Проанализируем ситуацию с точки зрения интерфейса. Нужно ли пользователю одновременно видеть 60 000 строк? Ответ — нет. Даже если дерево отображается «развернутым», человек физически не сможет обработать такой объем информации. Оптимальным решением будет загрузка данных только в те ветки, которые пользователь реально открывает.
Алгоритм действий следующий:
Родитель = Справочники.Номенклатура.ПустаяСсылка()).ПриРазворачивании (для элемента формы «Поле дерева значений») проверяем, загружены ли данные. Если нет — удаляем заглушку и подтягиваем реальных «детей» одним быстрым запросом по родителю.Рассмотрим пример кода для события:
Процедура ДеревоНоменклатурыПриРазворачивании(Элемент, Строка, Отказ)
ТекущаяСтрока = Строка;
// Если данные уже подгружены (проверяем по какому-либо флагу или количеству строк)
Если ТекущаяСтрока.Строки.Количество() = 1 И ТекущаяСтрока.Строки[0].Номенклатура = Неопределено Тогда
ТекущаяСтрока.Строки.Очистить();
// Выполняем быстрый запрос по родителю
Запрос = Новый Запрос("ВЫБРАТЬ Ссылка КАК Номенклатура ИЗ Справочник.Номенклатура ГДЕ Родитель = &Родитель");
Запрос.УстановитьПараметр("Родитель", ТекущаяСтрока.Номенклатура);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТекущаяСтрока.Строки.Добавить();
НоваяСтрока.Номенклатура = Выборка.Номенклатура;
// Добавляем заглушку, если это группа
Если Выборка.Номенклатура.ЭтоГруппа Тогда
НоваяСтрока.Строки.Добавить();
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Система компоновки данных (СКД) умеет работать с иерархией чрезвычайно быстро. Даже если вам не нужен отчет как таковой, вы можете программно использовать возможности СКД для трансформации плоской ТаблицаЗначений в ДеревоЗначений.
Рассмотрим этот процесс подробнее:
Выясним причину, почему это работает быстро. СКД использует внутренние механизмы индексации и оптимизированные итераторы. Мы можем передать в СКД таблицу, полученную из внешнего источника, настроить в схеме группировку «Иерархия» и использовать ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений. Это позволяет избежать написания сложных рекурсивных функций вручную.
Если специфика задачи все же требует ручного заполнения ДеревоЗначений на основе ТаблицаЗначений, критически важно использовать индексацию. В 1С 8.3 объекты типа ТаблицаЗначений позволяют создавать индексы.
Проанализируем шаги оптимизации:
Родитель.ТЗ.Индексы.Добавить("Родитель").ТЗ.НайтиСтроки(Новый Структура("Родитель", ТекущийУзел)).Без индекса метод НайтиСтроки будет каждый раз полностью сканировать таблицу. С индексом — обращение будет происходить мгновенно.
Для очень больших структур (более 100 000 строк) часто выгоднее отказаться от объекта ДеревоЗначений в пользу ТаблицаЗначений. Визуально иерархия имитируется с помощью специальной колонки «Отступ» или «Уровень», а раскрытие/сворачивание реализуется через фильтрацию строк (установка свойства Видимость). Платформа 1С отрисовывает плоские списки значительно быстрее, чем глубокие деревья, так как не тратит ресурсы на расчет геометрии связей между узлами.
Резюме: Чтобы добиться максимальной скорости, всегда старайтесь использовать метод Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкамСИерархией). Если же данных критически много — переходите на динамическую подгрузку узлов при разворачивании. Это обеспечит моментальный отклик интерфейса для пользователя.