При работе с устаревшими, но все еще актуальными конфигурациями на обычных формах (платформа 8.2 или 8.3 в режиме совместимости), программисты часто сталкиваются с проблемой производительности интерфейса. Типичная ситуация: необходимо показать пользователю список из 200 000 позиций номенклатуры, но при этом наложить сложный программный отбор, который отсекает лишь малую часть записей. Использование стандартной ТаблицыЗначений в таких случаях приводит к колоссальному потреблению оперативной памяти и «зависанию» клиента, а привычный по управляемым формам объект ДинамическийСписок с произвольным запросом в классических обычных формах отсутствует в явном виде.
В этой статье мы подробно разберем, как обойти эти ограничения, рассмотрим проверенные архитектурные подходы и научимся работать с большими массивами данных, сохраняя отзывчивость интерфейса.
Прежде всего, проанализируем стандартные возможности. Объекты типа СправочникСписок или РегистрСведенийСписок в обычных формах сами по себе являются динамическими — они считывают данные из базы порциями. Если ваш отбор можно выразить через простые равенства или вхождения в список, это самый быстрый путь.
Рассмотрим пример программной установки отбора в форме списка, когда нам нужно отфильтровать данные по конкретным критериям перед открытием:
// Пример установки отбора при открытии формы списка
ФормаСписка = Справочники.Номенклатура.ПолучитьФормуСписка(, ЭтаФорма);
// Устанавливаем вид сравнения и значение
ФормаСписка.Отбор.Ссылка.ВидСравнения = ВидСравнения.ВСписке;
ФормаСписка.Отбор.Ссылка.Значение = МассивНужныхСсылок; // Осторожно при больших объемах!
ФормаСписка.Отбор.Ссылка.Использование = Истина;
ФормаСписка.Открыть();
Важный нюанс: как было отмечено в обсуждении, если МассивНужныхСсылок содержит десятки тысяч элементов, этот метод станет «узким местом». Платформа будет генерировать SQL-запрос с огромным количеством параметров в условии WHERE IN (...), что может привести к деградации производительности всей СУБД.
Если стандартного функционала СправочникСписок недостаточно, проанализируем возможности объекта ПостроительОтчета. В обычных формах это наиболее мощный инструмент, который позволяет эмулировать поведение динамического списка с произвольным запросом.
Разберем по шагам, как связать запрос с интерфейсом:
ТабличноеПоле.Рассмотрим пример кода для инициализации такого списка:
Построитель = Новый ПостроительОтчета;
Построитель.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка,
| Номенклатура.Код,
| Номенклатура.Наименование
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивФильтра)";
Построитель.Параметры.Вставить("МассивФильтра", СписокСсылок);
Построитель.Выполнить();
// Привязываем результат к табличному полю
ЭлементыФормы.ТабличноеПолеСписок.Значение = Построитель.Результат.Выгрузить();
ЭлементыФормы.ТабличноеПолеСписок.СоздатьКолонки();
Однако, чтобы не грузить все 180 000 строк в память (что и требовалось автору вопроса), мы можем использовать ПостроительЗапроса и выводить данные порционно или использовать виртуализацию.
Проанализируем ситуацию, когда массив ссылок для отбора слишком велик для передачи в запрос. В этом случае эффективным решением будет использование технического регистра сведений. Это позволяет перенести тяжелую операцию фильтрации с уровня кода 1С на уровень индексов SQL-сервера.
Алгоритм действий следующий:
СоставСписковПользователей с измерениями Пользователь (тип Ссылка) и Объект (тип Ссылка на Номенклатуру).В (ВЫБРАТЬ Объект ИЗ РегистрСведений.СоставСписковПользователей ГДЕ Пользователь = &ТекущийПользователь).Этот метод позволяет платформе работать максимально быстро, так как SQL-сервер использует оптимальный план запроса на основе индексов таблиц БД, а не перебирает гигантский текстовый список параметров.
Выясним, как минимизировать потребление памяти, если нам всё же приходится использовать ТаблицуЗначений. Основная проблема — хранение всех данных (строк, чисел, дат) для каждой из 180 000 записей. Мы можем хранить в ТаблицеЗначений только ключевое поле (например, Ссылка).
Рассмотрим реализацию подгрузки данных «на лету»:
Процедура ТабличноеПолеПриВыводеСтроки(Элемент, ОформлениеСтроки, ДанныеСтроки)
// ДанныеСтроки содержат только Ссылку
ТекСсылка = ДанныеСтроки.Ссылка;
// Подтягиваем только нужные данные для отображения конкретной строки
// В реальной задаче лучше использовать кэширование или запрос к выборке
ОформлениеСтроки.Ячейки.Наименование.УстановитьТекст(ТекСсылка.Наименование);
ОформлениеСтроки.Ячейки.Артикул.УстановитьТекст(ТекСсылка.Артикул);
КонецПроцедуры
Важно: Прямое обращение через точку в ПриВыводеСтроки вызовет тысячи мелких запросов к базе. Чтобы этого избежать, используйте объект Соответствие для кэширования данных или предварительную выборку данных для видимой области экрана.
Как справедливо заметили участники обсуждения, в платформе 8.2 и выше существует возможность использовать управляемые формы даже в конфигурациях, работающих в обычном режиме. Это позволяет задействовать полноценный объект ДинамическийСписок с его мощным механизмом ПроизвольныйЗапрос.
Чтобы реализовать этот подход, выполним следующие шаги:
ДинамическийСписок.Это самый современный и технологичный способ решения задачи, так как он снимает нагрузку с клиента и передает всю работу по пагинации (постраничному выводу) и фильтрации серверу приложений и СУБД.
Разберем, какой метод выбрать в зависимости от условий:
Отбор в форме списка.ПостроительОтчета.Помните, что комфортная работа пользователя напрямую зависит от скорости отрисовки интерфейса. Избегайте полной выгрузки сотен тысяч строк в ТаблицуЗначений, отдавая предпочтение динамическим методам работы с данными.