Часто разработчики 1С сталкиваются с ситуацией, когда форма, содержащая динамический список, открывается недопустимо долго, например, 7-9 секунд. При этом замер производительности показывает, что основное время уходит на выполнение метода Форма.Открыть(), а текст запроса динамического списка, выполненный в консоли, отрабатывает почти мгновенно — поможет инструментарий для отладки и оптимизации решений в 1С. Это вызывает недоумение: если запрос быстрый, то что же так тормозит?
Давайте вместе разберемся в причинах такого поведения и рассмотрим несколько эффективных способов ускорения открытия таких форм. Проанализируем ситуацию, когда проблема кроется не в самом тексте запроса, а в том, как платформа 1С работает с динамическими списками и их отображением.
Ключевое заблуждение — считать, что запрос, который выполняется на форме, идентичен тому, что мы тестируем в консоли. На самом деле, платформа проделывает огромную "невидимую" работу, прежде чем отправить финальный запрос в СУБД и отобразить данные пользователю.
Вот что происходит "под капотом":
Элементы.МойСписок.Видимость = Ложь) дает гораздо больший эффект, чем скрытие отдельных колонок.В итоге, на сервер СУБД уходит сильно измененный и усложненный запрос, план выполнения которого может кардинально отличаться от плана исходного "чистого" запроса из консоли.
Один из самых сильных факторов, влияющих на скорость, — это режим отображения динамического списка, особенно когда используются группировки.
Дерево vs. Иерархический список
В свойствах элемента формы "Таблица" для динамического списка есть свойство Отображение. Два самых ресурсоемких варианта — это Дерево и Иерархический список.
Практический шаг: Если ваша форма открывается долго, а список отображается как дерево, попробуйте изменить его свойство Отображение на Иерархический список. Как показала практика, это простое действие может ускорить открытие формы в несколько раз.
Хотя исходный запрос может казаться быстрым, некоторые его конструкции становятся "бутылочным горлышком", когда платформа добавляет свою логику. Обратим внимание на проблемные места, которые были выявлены в ходе анализа.
Конструкция ВЫБОР КОГДА
Использование вычисляемых полей прямо в запросе динамического списка — частая причина замедления. Рассмотрим пример:
ВЫБРАТЬ
...
ВЫБОР
КОГДА ЗадачаЗадачиПользователя.БизнесПроцесс ССЫЛКА БизнесПроцесс.DX_ОбработкаТикета
ТОГДА ВЫРАЗИТЬ(ЗадачаЗадачиПользователя.БизнесПроцесс КАК БизнесПроцесс.DX_ОбработкаТикета).Контрагент
КОГДА ЗадачаЗадачиПользователя.БизнесПроцесс ССЫЛКА БизнесПроцесс.DX_Закупка
ТОГДА "Закупки"
ИНАЧЕ ИСТИНА
КОНЕЦ КАК ГруппировочноеПоле
ИЗ
Задача.ЗадачиПользователя КАК ЗадачаЗадачиПользователя
...
Проблема в том, что СУБД не может эффективно использовать индексы для таких полей. При попытке отсортировать или сгруппировать данные по полю ГруппировочноеПоле, скорее всего, будет происходить полное сканирование таблицы (Full Table Scan), что очень медленно на больших объемах данных.
Решение: Лучшим подходом является предварительный расчет таких данных и сохранение их в отдельный регистр сведений. Тогда в динамическом списке вы будете делать простое соединение с уже готовыми данными, что работает на порядки быстрее.
Коррелирующие подзапросы в условии ГДЕ
Еще одна потенциально медленная конструкция — это подзапрос в условии, который ссылается на поля из основного запроса. Например:
ГДЕ
(ЗадачаЗадачиПользователя.Исполнитель = &Исполнитель
ИЛИ ЗадачаЗадачиПользователя.DX_РольИсполнителя В
(ВЫБРАТЬ
DX_РегистрАдресацииЗадач.DX_РольИсполнителя
ИЗ
РегистрСведений.DX_РегистрАдресацииЗадач КАК DX_РегистрАдресацииЗадач
ГДЕ
DX_РегистрАдресацииЗадач.Исполнитель = &Исполнитель
И DX_РегистрАдресацииЗадач.DX_РольИсполнителя В (&ВыбранныеРоли)))
Фактически, такой подзапрос может выполняться для каждой строки основного запроса. Хотя современные СУБД умеют оптимизировать такие конструкции, они остаются зоной риска. Более производительной альтернативой часто является использование временных таблиц и явных соединений (ЛЕВОЕ СОЕДИНЕНИЕ).
Интересный факт: для динамических списков с включенным свойством ДинамическоеСчитываниеДанных коррелирующие подзапросы могут быть не так страшны. Поскольку платформа считывает данные небольшими порциями (например, по 45 строк), подзапрос будет выполнен только для этой маленькой порции, а не для всей таблицы.
Этот метод не ускоряет полную загрузку данных, но значительно улучшает воспринимаемую производительность. Пользователь почти мгновенно видит окно формы и может с ним работать, а "тяжелый" список загружается с небольшой задержкой. Это избавляет от ощущения "зависшей" программы.
Суть метода проста: мы скрываем динамический список при создании формы на сервере и делаем его видимым уже после того, как форма отобразилась на клиенте.
Разберем по шагам:
ПриСозданииНаСервере и устанавливаем видимость нашего списка в Ложь.
ПриОткрытии и в нем возвращаем видимость списка в Истина.
Посмотрим на пример кода:
// В модуле формы
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Скрываем "тяжелый" список при инициализации формы на сервере.
// Это предотвратит ресурсоемкие операции по получению данных и отрисовке
// до того, как форма будет показана пользователю.
Элементы.СписокЗадач.Видимость = Ложь;
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// Отображаем список после того, как "каркас" формы уже открылся и доступен пользователю.
// Загрузка данных начнется в этот момент.
Элементы.СписокЗадач.Видимость = Истина;
КонецПроцедуры
Этот простой прием позволяет создать отзывчивый интерфейс даже для форм со сложными и "тяжелыми" динамическими списками. Пользователь сразу получает контроль над программой, а данные подгружаются фоном.
В заключение, отметим, что проблема медленных динамических списков — комплексная. Ее решение часто требует комбинированного подхода: рефакторинга запроса, правильной настройки свойств элемента формы и, в некоторых случаях, применения программных "хитростей" для улучшения взаимодействия с пользователем.