При разработке сложных интерфейсов в 1С:Управление нашей фирмой (УНФ) или других конфигурациях на платформе 1С 8.3 разработчики часто сталкиваются с проблемой низкой производительности динамических списков. Ситуация, когда запрос содержит 20 и более таблиц с десятками связей, является критической для быстродействия. Рассмотрим подробно, почему возникают такие задержки и как правильно перестроить запрос, чтобы список открывался мгновенно.
Проанализируем представленный запрос. В нем допущен ряд архитектурных ошибок, которые в совокупности заставляют СУБД выполнять колоссальный объем лишней работы. Основная проблема заключается в попытке получить «всё и сразу» в одном запросе динамического списка. Динамический список предназначен для постраничного отображения данных, но при наличии сложных соединений и вложенных выборок механизм оптимизации платформы перестает работать эффективно.
Разберем самую грубую ошибку в запросе — выборку табличных частей документа непосредственно в основном тексте. Рассмотрим фрагмент:
ДокументЗаказПокупателя.Запасы.(
Ссылка,
НомерСтроки,
Номенклатура,
Количество,
Цена,
Сумма
)
Когда вы выбираете табличную часть внутри запроса динамического списка, 1С преобразует это в LEFT JOIN на уровне SQL. Если у одного заказа 10 строк в запасах, то в итоговой выборке сервер сформирует 10 записей для одного документа. Платформа должна будет программно «схлопнуть» эти данные обратно в коллекцию. В динамических списках это категорически запрещено стандартами разработки 1С.
Как сделать правильно:
Запасы, Работы, Материалы и т.д.) из основного запроса.В исходном запросе мы видим множественные соединения с виртуальными таблицами остатков и оборотов без указания параметров:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыПокупателей.ОстаткиИОбороты(, , Авто, , ) КАК ЗаказыПокупателейОстаткиИОбороты
ПО (ЗаказыПокупателейОстаткиИОбороты.ЗаказПокупателя = ДокументЗаказПокупателя.Ссылка)
Проанализируем ситуацию: виртуальная таблица без параметров Период заставляет систему рассчитывать итоги на текущий момент времени. При каждом скроллинге списка СУБД может заново обращаться к таблицам итогов. Кроме того, использование ОстаткиИОбороты внутри соединения в динамическом списке часто приводит к сканированию всей таблицы индекса.
Рекомендации по оптимизации:
Остатки с параметром Период.Выясним причину, почему запрос автора содержит так много соединений. Это попытка собрать контактную информацию, статусы и суммы в одном месте. Самый эффективный способ оптимизации здесь — денормизация.
Создадим вспомогательный Регистр сведений (например, СостоянияЗаказовДляСписка), который будет обновляться при проведении документа Заказ покупателя. В этот регистр запишем все «тяжелые» поля:
Тогда запрос динамического списка превратится из «монстра» в простую выборку:
ВЫБРАТЬ
Документ.Ссылка,
РегистрСостояний.СуммаОплаты,
РегистрСостояний.ТелефонКонтрагента,
РегистрСостояний.СтатусНаряда
ИЗ
Документ.ЗаказПокупателя КАК Документ
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияЗаказов КАК РегистрСостояний
ПО Документ.Ссылка = РегистрСостояний.Заказ
Такой подход сокращает количество соединений с 20 до 1-2, что ускоряет работу в десятки раз.
Посмотрим на пример из запроса автора, где КИ соединяется 7 раз:
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК EmailКонтрагента
ПО ДокументЗаказПокупателя.Контрагент = EmailКонтрагента.Ссылка
И (EmailContragenta.Вид = ЗНАЧЕНИЕ(Справочник.ВидыКонтактнойИнформации.EmailКонтрагента))
Каждое такое соединение — это поиск по одной и той же физической таблице. SQL-сервер вынужден многократно обращаться к одним и тем же страницам данных. Проанализируем ситуацию: если нам нужно вывести 5 видов КИ, лучше соединиться один раз с подзапросом, который развернет КИ в одну строку, или использовать механизм дополнительных реквизитов, если конфигурация это позволяет.
Если некоторые поля (например, остатки или сложные статусы) нельзя денормировать, рассмотрим метод их получения «на лету». В платформе 8.3 для динамического списка можно использовать обработчик события ПриПолученииДанныхНаСервере.
Алгоритм действий:
ДолгКонтрагента).Пример программного кода:
&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
// Получаем массив ссылок текущей страницы
МассивСсылок = Строки.ПолучитьКлючи();
// Выполняем один быстрый запрос по этим ссылкам
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
// Заполняем данные в коллекции Строки
// ...
КонецПроцедуры
Проанализируем еще несколько моментов, которые помогут «вылечить» ваш список:
Основная таблица (в данном случае Документ.ЗаказПокупателя). Это позволит платформе использовать механизм FETCH FIRST и корректно строить индексы.Документ.Контрагент.КонтактноеЛицо.Представление. Каждая точка — это неявное левое соединение. В вашем запросе таких скрытых связей может быть больше, чем явных.ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ ...). Постарайтесь заменить их на прямые соединения с физическими таблицами, перенося условия в секцию ПО.Подведем итог: Оптимизация через временные таблицы (как спрашивал автор) в динамическом списке невозможна напрямую в тексте запроса. Основной путь решения — это сокращение количества связей за счет денормизации данных в отдельный регистр и строгое следование стандартам 1С (отказ от табличных частей в списке и минимизация обращений «через точку»).