Как в 1С:УТ ускорить поиск по номенклатуре с учетом вложенных групп?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

Стандартная задача при работе со справочником номенклатуры — организовать поиск так, чтобы при выборе группы отображались не только товары непосредственно в ней, но и во всех дочерних подгруппах. Простое решение «в лоб» — изменить в динамическом списке вид сравнения для поля Родитель с Равно на ВИерархии. Однако на больших базах это почти всегда приводит к серьезному замедлению работы и долгому открытию форм. Чтобы точно определить причины таких задержек, рекомендуется регулярно проводить замер производительности контура 1С и поиск узких мест.

Давайте разберемся, почему так происходит, проанализировав технические причины медленной работы условия «В иерархии», и какие есть более производительные способы решить эту задачу.

Причина медленной работы отбора «В Иерархии»

Когда мы используем условие Родитель ВИерархии <Группа>, система для каждой строки номенклатуры в базе данных должна выполнить рекурсивную проверку: принадлежит ли ее родитель указанной группе или одной из ее дочерних групп. На тысячах или десятках тысяч позиций это становится очень ресурсоемкой операцией, которая и вызывает «тормоза». Стоит отметить, что похожие проблемы часто возникают и в отчетности, где требуется ускорение формирования отчета "Валовая прибыль предприятия" или использование механизмов, позволяющих запускать привилегированные отчеты для обхода ограничений RLS.

Проанализируем несколько эффективных подходов к оптимизации, от простого к более сложному.

Решение 1: Замена поля отбора с «Родитель» на «Ссылка»

Первый и самый быстрый способ, который стоит попробовать, — это изменить поле, по которому применяется иерархический отбор. Вместо проверки родителя для каждого элемента, мы можем задать условие для самой ссылки элемента.

В этом случае СУБД может построить более оптимальный план запроса, сначала выбрав все необходимые элементы и группы из иерархии, а затем уже отобразив их. Это часто работает значительно быстрее.

Как реализовать: в коде, где устанавливается отбор, нужно поменять первый и пятый параметры в вызове функции УстановитьЭлементОтбораДинамическогоСписка.

Посмотрим на пример из исходной задачи. Вместо этого кода:


ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Форма.СписокНоменклатура,
	"Родитель",
	ОтборПоИерархииНоменклатуры,
	ВидСравненияКомпоновкиДанных.ВИерархии,
	"Родитель",
	Истина);

Используем следующий вариант:


ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Форма.СписокНоменклатура,
	"Ссылка", // Поле для отбора
	ОтборПоИерархииНоменклатуры,
	ВидСравненияКомпоновкиДанных.ВИерархии, // Вид сравнения
	"Ссылка", // Идентификатор элемента отбора
	Истина);

Преимущества:

  1. Простота реализации — меняется всего пара строк.
  2. Часто дает заметный прирост производительности без сложных доработок.

Недостатки:

  1. Эффективность может зависеть от версии платформы, СУБД и структуры данных. В некоторых случаях выигрыш будет незначительным.

Решение 2: Отбор по заранее подготовленному списку групп (наиболее эффективный)

Этот метод является самым надежным с точки зрения производительности, позволяя реализовать фильтрацию по иерархии без тормозов в УТ 11. Идея заключается в том, чтобы разбить сложную иерархическую операцию на два простых и быстрых шага:

  1. Шаг 1. С помощью отдельного легковесного запроса мы получаем список всех дочерних групп для текущей выбранной группы. Так как количество групп в справочнике обычно на порядки меньше, чем количество элементов номенклатуры, этот запрос выполняется практически мгновенно.
  2. Шаг 2. Мы устанавливаем отбор для основного динамического списка по полю Родитель, но уже с видом сравнения ВСписке, передавая в качестве значения подготовленный на первом шаге список групп.

Такой подход избавляет основной запрос от необходимости вычислять иерархию для каждой строки. Он просто выполняет быструю проверку на вхождение родителя в готовый «плоский» список.

Давайте рассмотрим реализацию по шагам. Весь код нужно разместить в процедуре расширения, которая устанавливает отбор, например, Расш1_УстановитьОтборПоИерархииНоменклатуры, перед вызовом стандартной функции.

Пример кода:


&Вместо("УстановитьОтборПоИерархииНоменклатуры")
Процедура Расш1_УстановитьОтборПоИерархииНоменклатуры(Форма)
	Если Не Форма.ИспользоватьФильтры Тогда
		Возврат;
	КонецЕсли;
	
	Если Не Форма.ВариантНавигации = ПредопределенноеЗначение("Перечисление.ВариантыНавигацииВФормахНоменклатуры.ПоИерархии") Тогда
		Возврат;
	КонецЕсли;
	
	ТекущаяГруппа = Форма.ТекущаяИерархияНоменклатуры;
	
	// Шаг 1: Запросом получаем все дочерние группы
	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	Номенклатура.Ссылка
		|ИЗ
		|	Справочник.Номенклатура КАК Номенклатура
		|ГДЕ
		|	Номенклатура.ЭтоГруппа = ИСТИНА
		|	И Номенклатура.Ссылка В ИЕРАРХИИ(&ТекущаяГруппа)";

	Запрос.УстановитьПараметр("ТекущаяГруппа", ТекущаяГруппа);

	РезультатЗапроса = Запрос.Выполнить();
	СписокГрупп = РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Ссылка");
	
	// Важно: в список нужно добавить и саму родительскую группу,
	// так как запрос вернет только вложенные.
	// Если же в иерархии есть и сама группа, этот код можно убрать.
	СписокГрупп.Добавить(ТекущаяГруппа);

	// Шаг 2: Устанавливаем отбор по списку
	ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Форма.СписокНоменклатура,
		"Родитель",
		СписокГрупп, // Передаем наш список
		ВидСравненияКомпоновкиДанных.ВСписке, // Используем вид сравнения "В СПИСКЕ"
		"Родитель",
		Истина);
КонецПроцедуры

Преимущества:

  1. Максимальная производительность. Этот метод дает наилучший результат на больших объемах данных.
  2. Надежность. Работает стабильно быстро независимо от сложности иерархии.

Дополнительная оптимизация: устраняем зависание при вводе текста в строке поиска

Часто возникает еще одна проблема, связанная с производительностью: при вводе текста в поле поиска форма пытается обновиться после каждого нажатия клавиши. Если выполняется поиск по номенклатуре в 1С:УТ 11.5 и ERP 2.5 на больших объемах данных, это приводит к полному «зависанию» интерфейса. В таких случаях может помочь расширенный поиск объекта в базе 1С по любой части строки или специализированный поиск номенклатуры по отсканированному штрихкоду для УТ 11.5.

Здесь есть два решения.

Вариант А: Поиск по кнопке

Самый простой способ — отключить мгновенное обновление.

  1. Добавьте на форму кнопку, например, «Найти».
  2. В свойствах поля ввода для поиска отключите событие ПриИзменении или уберите из него код обновления списка.
  3. Перенесите логику поиска и обновления динамического списка в обработчик нажатия кнопки «Найти».

Пользователь вводит текст, нажимает кнопку, и только после этого система выполняет затратный по времени запрос. Просто и эффективно.

Вариант Б: Отложенный поиск

Более элегантный и удобный для пользователя вариант. Суть в том, чтобы реализовать интерактивный поиск с задержкой (debounce), запуская его не мгновенно, а с небольшой задержкой после того, как пользователь прекратил ввод.

Алгоритм реализации:

  1. В обработчике события ПриИзменении поля поиска не вызываем процедуру поиска напрямую.
  2. Вместо этого мы подключаем однократный обработчик ожидания с небольшим интервалом, например, 0.7-1 секунда. ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.7, Истина).
  3. Перед подключением нового обработчика обязательно отключаем предыдущий, если он был. Это нужно, чтобы поиск не сработал несколько раз.
  4. Процедура ВыполнитьПоиск, которая будет вызвана обработчиком, уже будет содержать логику обновления динамического списка.

Таким образом, пока пользователь быстро печатает, система не выполняет никаких действий. Как только он делает паузу, срабатывает таймер и запускается поиск. Это значительно снижает нагрузку и делает интерфейс отзывчивым.

Итог: для решения проблемы медленного иерархического поиска в 1С всегда начинайте с простого (замена поля отбора на Ссылка), но будьте готовы применить более надежный метод с предварительным формированием списка групп. А для комфортной работы пользователей не забывайте про оптимизацию «живого» поиска с помощью кнопки или отложенного вызова.

← На главную