← Назад к вопросам

Как оптимизировать сложные запросы в динамических списках?

2.7 Senior🔥 181 комментариев
#Запросы и оптимизация#Формы и интерфейс

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Оптимизация сложных запросов в динамических списках

Динамические списки — одна из самых узких мест по производительности в 1С. Требуется системный подход от архитектуры до SQL.

Проблема: Природа динамических списков

Когда это медленно:

  1. Большие объёмы данных (100K+ записей)
  2. Сложные условия отбора (ВЫБРАТЬ ГДЕ с вложенными запросами)
  3. Частые фильтрации и сортировки пользователем
  4. Агрегация данных (ИТОГО ПО, количество сторок)

Уровень 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, потом оптимизируй по результатам.