Как оптимизировать медленный запрос в динамическом списке 1С 8.3 при большом количестве связей и таблиц?

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

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

Анализ основной причины низкой производительности

Проанализируем представленный запрос. В нем допущен ряд архитектурных ошибок, которые в совокупности заставляют СУБД выполнять колоссальный объем лишней работы. Основная проблема заключается в попытке получить «всё и сразу» в одном запросе динамического списка. Динамический список предназначен для постраничного отображения данных, но при наличии сложных соединений и вложенных выборок механизм оптимизации платформы перестает работать эффективно.

Решение 1: Удаление вложенных табличных частей

Разберем самую грубую ошибку в запросе — выборку табличных частей документа непосредственно в основном тексте. Рассмотрим фрагмент:


ДокументЗаказПокупателя.Запасы.(
    Ссылка,
    НомерСтроки,
    Номенклатура,
    Количество,
    Цена,
    Сумма
)

Когда вы выбираете табличную часть внутри запроса динамического списка, 1С преобразует это в LEFT JOIN на уровне SQL. Если у одного заказа 10 строк в запасах, то в итоговой выборке сервер сформирует 10 записей для одного документа. Платформа должна будет программно «схлопнуть» эти данные обратно в коллекцию. В динамических списках это категорически запрещено стандартами разработки 1С.

Как сделать правильно:

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

Решение 2: Оптимизация работы с виртуальными таблицами

В исходном запросе мы видим множественные соединения с виртуальными таблицами остатков и оборотов без указания параметров:


ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ЗаказыПокупателей.ОстаткиИОбороты(, , Авто, , ) КАК ЗаказыПокупателейОстаткиИОбороты
ПО (ЗаказыПокупателейОстаткиИОбороты.ЗаказПокупателя = ДокументЗаказПокупателя.Ссылка)

Проанализируем ситуацию: виртуальная таблица без параметров Период заставляет систему рассчитывать итоги на текущий момент времени. При каждом скроллинге списка СУБД может заново обращаться к таблицам итогов. Кроме того, использование ОстаткиИОбороты внутри соединения в динамическом списке часто приводит к сканированию всей таблицы индекса.

Рекомендации по оптимизации:

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

Решение 3: Денормизация данных через «Регистр состояний»

Выясним причину, почему запрос автора содержит так много соединений. Это попытка собрать контактную информацию, статусы и суммы в одном месте. Самый эффективный способ оптимизации здесь — денормизация.

Создадим вспомогательный Регистр сведений (например, СостоянияЗаказовДляСписка), который будет обновляться при проведении документа Заказ покупателя. В этот регистр запишем все «тяжелые» поля:

Тогда запрос динамического списка превратится из «монстра» в простую выборку:


ВЫБРАТЬ
    Документ.Ссылка,
    РегистрСостояний.СуммаОплаты,
    РегистрСостояний.ТелефонКонтрагента,
    РегистрСостояний.СтатусНаряда
ИЗ
    Документ.ЗаказПокупателя КАК Документ
    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СостоянияЗаказов КАК РегистрСостояний
    ПО Документ.Ссылка = РегистрСостояний.Заказ

Такой подход сокращает количество соединений с 20 до 1-2, что ускоряет работу в десятки раз.

Решение 4: Оптимизация соединений с контактной информацией

Посмотрим на пример из запроса автора, где КИ соединяется 7 раз:


ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты.КонтактнаяИнформация КАК EmailКонтрагента
ПО ДокументЗаказПокупателя.Контрагент = EmailКонтрагента.Ссылка
И (EmailContragenta.Вид = ЗНАЧЕНИЕ(Справочник.ВидыКонтактнойИнформации.EmailКонтрагента))

Каждое такое соединение — это поиск по одной и той же физической таблице. SQL-сервер вынужден многократно обращаться к одним и тем же страницам данных. Проанализируем ситуацию: если нам нужно вывести 5 видов КИ, лучше соединиться один раз с подзапросом, который развернет КИ в одну строку, или использовать механизм дополнительных реквизитов, если конфигурация это позволяет.

Решение 5: Использование события ПриПолученииДанныхНаСервере

Если некоторые поля (например, остатки или сложные статусы) нельзя денормировать, рассмотрим метод их получения «на лету». В платформе 8.3 для динамического списка можно использовать обработчик события ПриПолученииДанныхНаСервере.

Алгоритм действий:

  1. Убираем из основного запроса сложные расчетные поля (например, ДолгКонтрагента).
  2. В обработчике получаем массив ссылок, которые сейчас отображаются на экране (обычно это 30-50 записей).
  3. Одним быстрым запросом получаем данные для этих ссылок и заполняем их в оформление строк.

Пример программного кода:


&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
    // Получаем массив ссылок текущей страницы
    МассивСсылок = Строки.ПолучитьКлючи();
    
    // Выполняем один быстрый запрос по этим ссылкам
    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ ... ГДЕ Ссылка В (&МассивСсылок)";
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    
    // Заполняем данные в коллекции Строки
    // ...
КонецПроцедуры

Важные технические советы

Проанализируем еще несколько моментов, которые помогут «вылечить» ваш список:

Подведем итог: Оптимизация через временные таблицы (как спрашивал автор) в динамическом списке невозможна напрямую в тексте запроса. Основной путь решения — это сокращение количества связей за счет денормизации данных в отдельный регистр и строгое следование стандартам 1С (отказ от табличных частей в списке и минимизация обращений «через точку»).

← На главную