При разработке интерфейсов на обычных формах в платформе 1С:Предприятие часто возникает задача отобразить в динамическом списке документов или справочников дополнительные сведения из периодических регистров сведений (например, текущие статусы, остатки или фактически отработанное время). Традиционно для этого используется событие табличного поля ПриПолученииДанных.
Однако при попытке переписать неоптимальный запрос в цикле на единый запрос разработчики нередко сталкиваются с парадоксом: пакетный запрос со списком ссылок может отрабатывать медленнее, чем точечные запросы в цикле. Разберем подробно, почему так происходит, как правильно составить запрос и какие приемы позволяют добиться максимальной скорости отрисовки строк списка.
Проанализируем две типичные ошибки, которые приводят к замедлению работы единого запроса в процедуре ПриПолученииДанных:
СрезПоследних условие на измерение накладывается в блоке ГДЕ, платформа сначала строит срез последних по всей таблице регистра в базе данных и лишь затем отсекает лишние записи. Перенос отбора в параметры виртуальной таблицы заставляет СУБД фильтровать данные на начальном этапе.Выгрузить() и последующий ТаблицаЗначений.Найти() внутри цикла обхода строк на клиенте выполняют линейный перебор строк в памяти, создавая заметную задержку.РаботаПо = &РаботаПо СУБД использует быстрый поиск по индексу (Index Seek). При передаче списка РаботаПо В (&СписокЗадач) может происходить сканирование диапазонов (Index Scan) или неоптимальные соединения во вложенных циклах. Кроме того, единичный запрос при повторении часто берет данные из буферного кэша СУБД.Чтобы избежать запросов в цикле (которые вызывают лавинообразный рост сетевых раунд-трипов при реальной клиент-серверной работе) и избавиться от медленного поиска по таблице значений, применим объект Соответствие и легкий Массив вместо тяжелого СписокЗначений.
Посмотрим на правильный паттерн реализации:
Процедура ДокументСписокПриПолученииДанных(Элемент, ОформленияСтрок)
СоответствиеСтрок = Новый Соответствие;
МассивСсылок = Новый Массив;
// Формируем массив для запроса и карту соответствия ссылки объекту оформления строки
Для Каждого Оформление Из ОформленияСтрок Цикл
Ссылка = Оформление.ДанныеСтроки.Ссылка;
МассивСсылок.Добавить(Ссылка);
СоответствиеСтрок.Вставить(Ссылка, Оформление);
КонецЦикла;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Срез.РаботаПо КАК Задача,
| Срез.ВыполненоНаСегодня КАК ВыполненоНаСегодня
|ИЗ
| РегистрСведений.ВремяРаботыТехподдержки.СрезПоследних(, РаботаПо В (&МассивСсылок)) КАК Срез";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Оформление = СоответствиеСтрок.Получить(Выборка.Задача);
Если Оформление <> Неопределено Тогда
Оформление.Ячейки.Выполнено.Значение = Выборка.ВыполненоНаСегодня;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
В этом варианте выборка обходится последовательно, а доступ к нужному объекту ОформлениеСтроки происходит за мгновенное время благодаря хеш-таблице объекта Соответствие. Присвоение свойства Значение также работает быстрее, чем вызов метода УстановитьТекст().
Разберем, почему процедура может вызываться 2–3 раза подряд при открытии или скроллинге списка (например, сначала для 26 строк, затем для 3 и для 1):
Если виртуальная таблица СрезПоследних со списком условий все равно создает задержки на больших объемах регистра, рассмотрим дополнительные архитектурные решения:
1. Кэширование на уровне модуля формы:
Объявим в начале модуля формы переменную КэшДанных = Новый Соответствие;. В процедуре ПриПолученииДанных будем сначала проверять наличие значения в кэше, а в запрос передавать исключительно те ссылки, которых еще нет в памяти. Сброс кэша выполняется при интерактивном обновлении списка или при записи изменений.
2. Ручной расчет среза через физическую таблицу:
При сложных срезах эффективнее поместить массив ссылок во временную таблицу, соединить ее с реальной физической таблицей регистра, сгруппировать по измерению для получения МАКСИМУМ(Период), а затем вторым соединением получить итоговые ресурсы. Такой подход гарантирует использование оптимизатором СУБД самых эффективных индексов.
3. Денормализация данных:
Для высоконагруженных списков лучшей практикой является хранение итогового состояния или времени непосредственно в реквизите документа либо в непериодическом регистре с ключом 1:1, обновляемом при проведении. Это позволяет списку читать данные напрямую без ресурсоемких расчетов при отрисовке интерфейса.