Как оптимизировать отбор в динамическом списке 1С при работе с большими объемами данных

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов на управляемых формах 1С часто возникает задача отображения больших списков документов или справочников с миллионами записей. Если в динамическом списке используется произвольный запрос с пакетом временных таблиц, соединениями или разыменованием ссылочных полей через точку, установка пользовательского или программного отбора может приводить к критическим задержкам (вплоть до десятков секунд). Разберем причины такого поведения платформы и подробно рассмотрим эффективные способы оптимизации.

Выясним причины замедления работы отборов

Для понимания проблемы проанализируем, как платформа 1С и система компоновки данных (СКД) обрабатывают динамические списки:

  1. Оборачивание запроса механизмом СКД во внешний вложенный запрос. Когда отбор накладывается через свойство Список.Отбор, платформа не всегда способна автоматически протолкнуть условие внутрь временных таблиц сложного пакета. В результате весь пакет временных таблиц рассчитывается целиком по всему объему таблицы (например, по 1,5 миллионам документов), и только к итоговому результату применяется внешнее условие фильтрации.
  2. Потеря порционного динамического считывания данных. Если для динамического списка с произвольным запросом не указана Основная таблица, платформа теряет возможность выполнять динамическую выборку порциями (keyset pagination) по индексам. При каждом обновлении или изменении отбора СУБД вынуждена выполнять полный пересчет запроса.
  3. Неявные соединения (разыменование через точку). Использование условий вида ТипЗаявки.Родитель = &Значение на уровне СУБД приводит к неявному левому соединению (LEFT JOIN). Если реквизит ТипЗаявки в документе не проиндексирован, СУБД сканирует всю таблицу целиком (Table Scan / Clustered Index Scan).
  4. Особенности сортировки. Сложная сортировка по полям составного типа или неиндексированным реквизитам заставляет СУБД выполнять предварительную сортировку всех строк до применения отсечения страниц.

Способ 1. Использование расширения языка запросов СКД

Чтобы условия отбора динамического списка применялись непосредственно на этапе сбора данных во временные таблицы, а не к финальному результату пакета, используем конструкции расширения языка запросов СКД с фигурными скобками {ГДЕ ...}.

Рассмотрим пример правильной структуры запроса динамического списка:


ВЫБРАТЬ
    Заявка.Ссылка КАК Ссылка,
    Заявка.Номер КАК Номер,
    Заявка.Дата КАК Дата,
    Заявка.ТипЗаявки КАК ТипЗаявки
ПОМЕСТИТЬ ВТ_ОтфильтрованныеЗаявки
ИЗ
    Документ.Заявка КАК Заявка
{ГДЕ
    Заявка.ТипЗаявки.* КАК ТипЗаявки,
    Заявка.ТипЗаявки.Родитель.* КАК ТипЗаявкиРодитель}
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Ссылка КАК Ссылка,
    ВТ.Номер КАК Номер,
    ВТ.Дата КАК Дата,
    ВТ.ТипЗаявки КАК ТипЗаявки
ИЗ
    ВТ_ОтфильтрованныеЗаявки КАК ВТ

Благодаря блоку {ГДЕ ...} система компоновки данных встроит условие отбора списка внутрь первого запроса пакета, что сократит объем промежуточных данных во временной таблице.

Способ 2. Замена разыменования полей на отбор по списку прямых ссылок

Проанализируем обращение к реквизитам через точку в условиях отбора. Конструкция ТипЗаявки.Родитель требует выполнения соединения таблиц. Для ускорения заменим проверку родителя на прямое сравнение со списком всех его дочерних элементов.

Посмотрим на пример программной установки отбора:


// 1. Получаем массив всех элементов нужной группы
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТипыЗаявок.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.ТипыЗаявок КАК ТипыЗаявок
    |ГДЕ
    |	ТипыЗаявок.Родитель = &РодительИерархии";

Запрос.УстановитьПараметр("РодительИерархии", ПредопределенноеЗначение("Справочник.ТипыЗаявок.СервисныеЗаявки"));
СписокТипов = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");

// 2. Устанавливаем отбор по прямому полю через список
ОтборСписка = Список.Отбор.Элементы;
ОтборСписка.Очистить();

ЭлементОтбора = ОтборСписка.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ТипЗаявки");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке;
ЭлементОтбора.ПравоеЗначение = СписокТипов;
ЭлементОтбора.Использование = Истина;

Такой подход позволяет СУБД использовать прямой индекс по реквизиту ТипЗаявки без выполнения дополнительных соединений.

Способ 3. Передача условий через параметры динамического списка

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

В тексте запроса динамического списка задаем условие:


ВЫБРАТЬ
    Заявка.Ссылка КАК Ссылка,
    Заявка.Номер КАК Номер,
    Заявка.Дата КАК Дата,
    Заявка.ТипЗаявки КАК ТипЗаявки
ИЗ
    Документ.Заявка КАК Заявка
ГДЕ
    (&НеИспользоватьФильтрПоТипу ИЛИ Заявка.ТипЗаявки.Родитель = &РодительТипа)

На форме управляем значением параметра программно:


Список.Параметры.УстановитьЗначениеПараметра("РодительТипа", ПредопределенноеЗначение("Справочник.ТипыЗаявок.СервисныеЗаявки"));
Список.Параметры.УстановитьЗначениеПараметра("НеИспользоватьФильтрПоТипу", Ложь);

Способ 4. Проверка метаданных и свойств динамического списка

Разберем обязательные настройки динамического списка и объектов метаданных, влияющие на скорость выполнения запроса:

  1. Заполнение свойства «Основная таблица». В свойствах динамического списка обязательно укажем основную таблицу (например, Документ.Заявка). Это позволяет платформе генерировать оптимизированный SQL-запрос для порционного считывания данных.
  2. Проверка индексов реквизитов. Для всех полей, по которым часто накладывается отбор (например, ТипЗаявки), в дереве метаданных конфигуратора должно быть установлено свойство Индексировать или Индексировать с дополнительным упорядочиванием.
  3. Анализ плана выполнения в профайлере СУБД. Если стандартные замеры производительности в 1С показывают длительное выполнение на строке вызова списка, зафиксируем реальный SQL-запрос через технологический журнал 1С (событие DBMSSQL / DBPOSTGRS) или профайлер СУБД. Проанализируем план выполнения (Execution Plan) на наличие операций Table Scan и отсутствующих индексов.
  4. Отказ от избыточных временных таблиц. Если логика формы позволяет, перенесем тяжелые расчеты в отдельный индексный регистр сведений, чтобы динамический список выполнял простое чтение из одной физической таблицы без ресурсоемких соединений.
← На главную