Как оптимизировать сложные запросы в динамических списках?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Оптимизация сложных запросов в динамических списках
Динамические списки — одна из самых узких мест по производительности в 1С. Требуется системный подход от архитектуры до SQL.
Проблема: Природа динамических списков
Когда это медленно:
- Большие объёмы данных (100K+ записей)
- Сложные условия отбора (ВЫБРАТЬ ГДЕ с вложенными запросами)
- Частые фильтрации и сортировки пользователем
- Агрегация данных (ИТОГО ПО, количество сторок)
Уровень 1: Правильная архитектура запроса
Правило 1: Ограничение количества строк
Список.ПостройтеДинамическиеПоля();
Список.Параметры.УстановитьЗначениеПараметра("СортировкаВверху", 1);
// ВАЖНО: Ограничение количества выбираемых строк
Список.ЗапросПостроения.НавигацияПоЗаписям = Истина;
Список.ЗапросПостроения.СоставляетсяВремя = Истина;
Правило 2: Выбирай только нужные колонки
// ❌ Плохо — выбирает ВСЕ колонки
ТекстЗапроса =
"ВЫБРАТЬ *
|ИЗ
| Справочник.Контрагенты";
// ✅ Хорошо — выбирает конкретные
ТекстЗапроса =
"ВЫБРАТЬ
| Ссылка,
| Код,
| Наименование,
| ИНН
|ИЗ
| Справочник.Контрагенты";
Уровень 2: Оптимизация SQL на уровне платформы
Пример: Список документов с фильтрацией
// ❌ Неоптимизированный вариант (МЕДЛЕННО)
ТекстЗапроса =
"ВЫБРАТЬ
| Документы.Ссылка,
| Документы.Номер,
| Документы.Дата,
| Документы.Сумма,
| КОЛ(ИСТИНА) КАК КоличествоСтрок
|ИЗ
| Документ.Счёт КАК Документы
|ГДЕ
| Документы.Дата >= &НачалоПериода
| И Документы.Дата < &КонецПериода
| И Документы.Контрагент В
| (ВЫБРАТЬ Ссылка
| ИЗ Справочник.Контрагенты
| ГДЕ ВидДеятельности = &ВидДеятельности)
|ИТОГО ПО Документы.Контрагент";
// ✅ Оптимизированный вариант (БЫСТРО)
ТекстЗапроса =
"ВЫБРАТЬ
| Документы.Ссылка,
| Документы.Номер,
| Документы.Дата,
| Документы.Сумма
|ИЗ
| Документ.Счёт КАК Документы
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ
| Справочник.Контрагенты КАК Контрагенты
| ПО Документы.Контрагент = Контрагенты.Ссылка
|ГДЕ
| Документы.Дата >= &НачалоПериода
| И Документы.Дата < &КонецПериода
| И Контрагенты.ВидДеятельности = &ВидДеятельности";
Почему это быстрее:
- ВНУТРЕННЕЕ СОЕДИНЕНИЕ вместо подзапроса
- Без ИТОГО (считай сам в коде, если нужно)
- Явное ограничение ПЕРВЫЕ N
Уровень 3: Индексирование
Создание индексов на часто используемые фильтры
В конфигураторе → Таблица БД → Индексы:
Индекс: "ИндексПоДате"
Поля: Дата, Контрагент
Индекс: "ИндексПоКонтрагенту"
Поля: Контрагент, Дата DESC
SQL для проверки индексов в PostgreSQL:
SELECT
schemaname,
tablename,
indexname
FROM pg_indexes
WHERE tablename = '_Document123' -- 1С код для таблицы
ORDER BY tablename, indexname;
Уровень 4: Кеширование результатов
Использование таблицы значений для промежуточных результатов
Процедура ОптимизированнойСписок() Экспорт
// Этап 1: Кешируем основные данные в таблицу значений
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Контрагент");
ТаблицаДанных.Колонки.Добавить("Наименование");
ТаблицаДанных.Колонки.Добавить("Сумма");
// Этап 2: Выполняем один ТЯЖЁЛЫЙ запрос
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Документы.Контрагент,
| Контрагенты.Наименование,
| СУММА(Документы.Сумма) КАК Сумма
|ИЗ
| Документ.Счёт КАК Документы
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ
| Справочник.Контрагенты КАК Контрагенты
| ПО Документы.Контрагент = Контрагенты.Ссылка
|ГДЕ
| Документы.Дата >= &ДатаОт
|СГРУППИРОВАТЬ ПО
| Документы.Контрагент,
| Контрагенты.Наименование
|УПОРЯДОЧИТЬ ПО
| Сумма УБЫВ";
Запрос.УстановитьПараметр("ДатаОт", НачалоДня(ТекущаяДата()));
ТаблицаДанных = Запрос.Выполнить().Выгрузить();
// Этап 3: Привязываем к форме
ЭлементыФормы.Список.Источник = ТаблицаДанных;
КонецПроцедуры;
Уровень 5: Асинхронная загрузка (фон 1С)
Использование фоновых работ для длительных запросов
Процедура ЗагрузитьДанныеВФоне() Экспорт
Параметры = Новый Структура("НачалоПериода,КонецПериода");
Параметры.НачалоПериода = НачалоДня(ТекущаяДата());
Параметры.КонецПериода = КонецДня(ТекущаяДата());
ИДЗадания = ФоновыеЗадания.Выполнить(
"МодульПрограммы.ПроцедураЗагрузки",
Параметры,,
"ОписаниеФоновойЗадачи"
);
// Показываем spinner пользователю
ЭлементыФормы.СтатусСообщение.Заголовок = "Загрузка данных...";
КонецПроцедуры;
Уровень 6: Мониторинг производительности
Использование встроенных инструментов:
// В конфигураторе: Вид → Тестирование и отладка → Анализатор
// Это показывает время выполнения каждого запроса
Операция = ОМ.ИнформацияОВыполнении("НайтиОсновнойДокумент");
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Время выполнения: " + Операция.ОбщееВремя + " мс";
Сообщение.Сообщить();
Чеклист оптимизации
- Ограничиваешь количество строк (ПЕРВЫЕ 100+)
- Выбираешь конкретные колонки (не SELECT *)
- Используешь ВНУТРЕННЕЕ СОЕДИНЕНИЕ (не IN подзапросы)
- Есть индексы на часто фильтруемые колонки
- Нет N+1 запросов в цикле
- Кешируешь результаты в ТабЗнач при частых обращениях
- Проверяешь план запроса в PostgreSQL (EXPLAIN)
- Используешь фоновые задачи для 5+ сек запросов
Вывод: Оптимизация динамических списков — это комбинация правильной архитектуры SQL, индексирования и кеширования. Начни с EXPLAIN ANALYZE в PostgreSQL, потом оптимизируй по результатам.