Стандартная задача при работе со справочником номенклатуры — организовать поиск так, чтобы при выборе группы отображались не только товары непосредственно в ней, но и во всех дочерних подгруппах. Простое решение «в лоб» — изменить в динамическом списке вид сравнения для поля Родитель с Равно на ВИерархии. Однако на больших базах это почти всегда приводит к серьезному замедлению работы и долгому открытию форм. Чтобы точно определить причины таких задержек, рекомендуется регулярно проводить замер производительности контура 1С и поиск узких мест.
Давайте разберемся, почему так происходит, проанализировав технические причины медленной работы условия «В иерархии», и какие есть более производительные способы решить эту задачу.
Когда мы используем условие Родитель ВИерархии <Группа>, система для каждой строки номенклатуры в базе данных должна выполнить рекурсивную проверку: принадлежит ли ее родитель указанной группе или одной из ее дочерних групп. На тысячах или десятках тысяч позиций это становится очень ресурсоемкой операцией, которая и вызывает «тормоза». Стоит отметить, что похожие проблемы часто возникают и в отчетности, где требуется ускорение формирования отчета "Валовая прибыль предприятия" или использование механизмов, позволяющих запускать привилегированные отчеты для обхода ограничений RLS.
Проанализируем несколько эффективных подходов к оптимизации, от простого к более сложному.
Первый и самый быстрый способ, который стоит попробовать, — это изменить поле, по которому применяется иерархический отбор. Вместо проверки родителя для каждого элемента, мы можем задать условие для самой ссылки элемента.
В этом случае СУБД может построить более оптимальный план запроса, сначала выбрав все необходимые элементы и группы из иерархии, а затем уже отобразив их. Это часто работает значительно быстрее.
Как реализовать: в коде, где устанавливается отбор, нужно поменять первый и пятый параметры в вызове функции УстановитьЭлементОтбораДинамическогоСписка.
Посмотрим на пример из исходной задачи. Вместо этого кода:
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Форма.СписокНоменклатура,
"Родитель",
ОтборПоИерархииНоменклатуры,
ВидСравненияКомпоновкиДанных.ВИерархии,
"Родитель",
Истина);
Используем следующий вариант:
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Форма.СписокНоменклатура,
"Ссылка", // Поле для отбора
ОтборПоИерархииНоменклатуры,
ВидСравненияКомпоновкиДанных.ВИерархии, // Вид сравнения
"Ссылка", // Идентификатор элемента отбора
Истина);
Преимущества:
Недостатки:
Этот метод является самым надежным с точки зрения производительности, позволяя реализовать фильтрацию по иерархии без тормозов в УТ 11. Идея заключается в том, чтобы разбить сложную иерархическую операцию на два простых и быстрых шага:
Родитель, но уже с видом сравнения ВСписке, передавая в качестве значения подготовленный на первом шаге список групп.Такой подход избавляет основной запрос от необходимости вычислять иерархию для каждой строки. Он просто выполняет быструю проверку на вхождение родителя в готовый «плоский» список.
Давайте рассмотрим реализацию по шагам. Весь код нужно разместить в процедуре расширения, которая устанавливает отбор, например, Расш1_УстановитьОтборПоИерархииНоменклатуры, перед вызовом стандартной функции.
Пример кода:
&Вместо("УстановитьОтборПоИерархииНоменклатуры")
Процедура Расш1_УстановитьОтборПоИерархииНоменклатуры(Форма)
Если Не Форма.ИспользоватьФильтры Тогда
Возврат;
КонецЕсли;
Если Не Форма.ВариантНавигации = ПредопределенноеЗначение("Перечисление.ВариантыНавигацииВФормахНоменклатуры.ПоИерархии") Тогда
Возврат;
КонецЕсли;
ТекущаяГруппа = Форма.ТекущаяИерархияНоменклатуры;
// Шаг 1: Запросом получаем все дочерние группы
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.ЭтоГруппа = ИСТИНА
| И Номенклатура.Ссылка В ИЕРАРХИИ(&ТекущаяГруппа)";
Запрос.УстановитьПараметр("ТекущаяГруппа", ТекущаяГруппа);
РезультатЗапроса = Запрос.Выполнить();
СписокГрупп = РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
// Важно: в список нужно добавить и саму родительскую группу,
// так как запрос вернет только вложенные.
// Если же в иерархии есть и сама группа, этот код можно убрать.
СписокГрупп.Добавить(ТекущаяГруппа);
// Шаг 2: Устанавливаем отбор по списку
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Форма.СписокНоменклатура,
"Родитель",
СписокГрупп, // Передаем наш список
ВидСравненияКомпоновкиДанных.ВСписке, // Используем вид сравнения "В СПИСКЕ"
"Родитель",
Истина);
КонецПроцедуры
Преимущества:
Часто возникает еще одна проблема, связанная с производительностью: при вводе текста в поле поиска форма пытается обновиться после каждого нажатия клавиши. Если выполняется поиск по номенклатуре в 1С:УТ 11.5 и ERP 2.5 на больших объемах данных, это приводит к полному «зависанию» интерфейса. В таких случаях может помочь расширенный поиск объекта в базе 1С по любой части строки или специализированный поиск номенклатуры по отсканированному штрихкоду для УТ 11.5.
Здесь есть два решения.
Самый простой способ — отключить мгновенное обновление.
ПриИзменении или уберите из него код обновления списка.Пользователь вводит текст, нажимает кнопку, и только после этого система выполняет затратный по времени запрос. Просто и эффективно.
Более элегантный и удобный для пользователя вариант. Суть в том, чтобы реализовать интерактивный поиск с задержкой (debounce), запуская его не мгновенно, а с небольшой задержкой после того, как пользователь прекратил ввод.
Алгоритм реализации:
ПриИзменении поля поиска не вызываем процедуру поиска напрямую.ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.7, Истина).ВыполнитьПоиск, которая будет вызвана обработчиком, уже будет содержать логику обновления динамического списка.Таким образом, пока пользователь быстро печатает, система не выполняет никаких действий. Как только он делает паузу, срабатывает таймер и запускается поиск. Это значительно снижает нагрузку и делает интерфейс отзывчивым.
Итог: для решения проблемы медленного иерархического поиска в 1С всегда начинайте с простого (замена поля отбора на Ссылка), но будьте готовы применить более надежный метод с предварительным формированием списка групп. А для комфортной работы пользователей не забывайте про оптимизацию «живого» поиска с помощью кнопки или отложенного вызова.