Как оптимизировать сложные запросы в 1С и ускорить работу системы?

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

При работе в среде 1С:Предприятие 8.3 разработчики часто сталкиваются с проблемой низкой производительности запросов. Особенно остро этот вопрос встает при увеличении объема базы данных или при необходимости построения сложных аналитических отчетов. Рассмотрим подробнее, как можно оптимизировать код, чтобы система работала быстрее, и проанализируем основные приемы работы с механизмами платформы.

Выясним причину: почему вложенные запросы замедляют работу?

Довольно часто в коде можно встретить использование вложенных запросов в секции ГДЕ или ИЗ. Проанализируем ситуацию: СУБД (например, MS SQL Server или PostgreSQL) при получении вложенного запроса не всегда может построить оптимальный план выполнения. Это приводит к тому, что временные данные вычисляются многократно или неэффективно используются индексы основных таблиц.

Разберем по шагам, какие проблемы создают вложенные запросы:

  1. Непредсказуемость плана запроса: Оптимизатор СУБД может ошибиться в оценке количества строк, возвращаемых вложенным запросом.
  2. Отсутствие индексов: Результат вложенного запроса — это виртуальная таблица, которая не имеет индексов, что делает соединения с ней крайне медленными.
  3. Избыточность вычислений: В некоторых случаях вложенный запрос может выполняться для каждой строки основного запроса.

Для решения этой проблемы мы рекомендуем использовать объект МенеджерВременныхТаблиц. Посмотрим на пример того, как правильно переписать тяжелый запрос.

Разберем по шагам: использование временных таблиц

Временные таблицы позволяют разбить один сложный запрос на несколько простых этапов. Это не только упрощает чтение кода, но и дает возможность явно индексировать промежуточные результаты. Рассмотрим типичный пример оптимизации.

Предположим, нам нужно получить остатки товаров только по тем позициям, которые входят в определенную категорию. Вместо вложенного запроса мы создадим временную таблицу.

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


// Создаем менеджер временных таблиц
МенеджерВТ = Новый МенеджерВременныхТаблиц;
Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = МенеджерВТ;

// Шаг 1: Выбираем нужные товары во временную таблицу
Запрос.Текст = 
"ВЫБРАТЬ
|	Товары.Ссылка КАК Товар
|ПОМЕСТИТЬ ВТ_НужныеТовары
|ИЗ
|	Справочник.Номенклатура КАК Товары
|ГДЕ
|	Товары.Категория = &Категория
|ИНДЕКСИРОВАТЬ ПО
|	Товар";

Запрос.УстановитьПараметр("Категория", ВыбраннаяКатегория);
Запрос.Выполнить();

// Шаг 2: Используем временную таблицу для получения остатков
Запрос.Текст = 
"ВЫБРАТЬ
|	Остатки.Номенклатура,
|	Остатки.КоличествоОстаток
|ИЗ
|	РегистрНакопления.ТоварыНаСкладах.Остатки( , Номенклатура В (ВЫБРАТЬ Товар ИЗ ВТ_НужныеТовары)) КАК Остатки";

Результат = Запрос.Выполнить().Выгрузить();

В данном примере использование предложения ИНДЕКСИРОВАТЬ ПО критически важно. Это позволяет СУБД мгновенно находить соответствия при соединении таблиц на втором этапе. Проанализируем: без индексации временной таблицы СУБД пришлось бы выполнять полное сканирование (Full Scan) данных в памяти, что при большом количестве строк привело бы к значительным задержкам.

Проанализируем работу с виртуальными таблицами

Виртуальные таблицы, такие как Остатки, ОстаткиИОбороты или СрезПоследних, являются мощным инструментом 1С. Однако неправильное их использование — одна из самых частых причин тормозов. Разберем основные правила работы с ними:

  1. Всегда используйте параметры: Никогда не фильтруйте данные виртуальных таблиц в секции ГДЕ. Используйте только параметры виртуальной таблицы.
  2. Фильтрация по измерениям: Старайтесь передавать в параметры условия по индексированным измерениям регистра.
  3. Избегайте обращений через точку: В условиях виртуальных таблиц обращения типа Номенклатура.Артикул заставляют систему выполнять неявные соединения таблиц, что сильно замедляет запрос.

Посмотрим на пример правильной фильтрации:


// ПЛОХО: Фильтрация в секции ГДЕ
Запрос.Текст = "ВЫБРАТЬ * ИЗ РегистрНакопления.Продажи.Обороты() КАК Продажи ГДЕ Продажи.Контрагент = &Контрагент";

// ХОРОШО: Фильтрация в параметрах таблицы
Запрос.Текст = "ВЫБРАТЬ * ИЗ РегистрНакопления.Продажи.Обороты(,,,&Контрагент) КАК Продажи";

Во втором случае 1С передает фильтр напрямую в SQL-запрос, и СУБД выбирает только нужные записи, не перебирая весь регистр.

Рассмотрим подробнее: типичные ошибки и "антипаттерны"

Существует несколько подходов, которые кажутся удобными, но на практике убивают производительность. Разберем их, чтобы не допускать в будущем.

Запросы в цикле. Это классическая ошибка. Проанализируем ситуацию: если у вас есть список из 1000 строк и в каждой итерации цикла выполняется Запрос.Выполнить(), это означает 1000 обращений к базе данных. Время тратится на установку соединения, передачу текста запроса, компиляцию плана и возврат данных.

Решение: Подготовьте все необходимые данные одним запросом заранее, выгрузите их в ТаблицаЗначений или Соответствие, а затем работайте с этими данными в оперативной памяти.

Использование ГДЕ Ссылка В (&Массив). Если в массиве передается более 1000–2000 элементов, запрос может начать деградировать. СУБД сложно обрабатывать огромные списки в условии IN.

Решение: Загрузите массив во временную таблицу и используйте соединение (ВНУТРЕННЕЕ СОЕДИНЕНИЕ) с этой таблицей.

Выборка лишних полей. Использование ВЫБРАТЬ * или выборка тяжелых полей (например, неограниченных строк или хранилищ значений) без необходимости увеличивает нагрузку на сеть и дисковую подсистему.

Выясним причину: когда стоит использовать индексацию?

Индексы — это "алфавитный указатель" для базы данных. Без них СУБД вынуждена просматривать каждую страницу данных (Table Scan). В 1С индексы создаются автоматически для всех измерений регистров, для кода и наименования справочников (если включены соответствующие настройки).

Однако при создании временных таблиц программист сам несет ответственность за наличие индексов. Если вы планируете в дальнейшем соединять временную таблицу с другими таблицами по какому-либо полю, это поле обязательно должно быть проиндексировано.

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


Запрос.Текст = 
"ВЫБРАТЬ
|	Заказы.Ссылка КАК Заказ,
|	Заказы.Контрагент КАК Контрагент
|ПОМЕСТИТЬ ВТ_Заказы
|ИЗ
|	Документ.ЗаказПокупателя КАК Заказы
|ИНДЕКСИРОВАТЬ ПО
|	Заказ, Контрагент;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ_Заказы.Контрагент,
|	ВТ_Заказы.Заказ,
|	Продажи.СуммаОборот
|ИЗ
|	ВТ_Заказы КАК ВТ_Заказы
|ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.Продажи.Обороты(,,, Контрагент В (ВЫБРАТЬ Контрагент ИЗ ВТ_Заказы)) КАК Продажи
|ПО ВТ_Заказы.Заказ = Продажи.ЗаказПокупателя";

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

Подведем итоги совместной работы

Мы проанализировали основные методы ускорения запросов в 1С. Подведем краткий итог того, что мы узнали:

  1. Всегда заменяйте сложные вложенные запросы на временные таблицы.
  2. Не забывайте использовать ИНДЕКСИРОВАТЬ ПО для полей связи во временных таблицах.
  3. Передавайте все возможные фильтры в параметры виртуальных таблиц, а не в секцию ГДЕ.
  4. Избегайте запросов в циклах — это главный враг производительности.
  5. Следите за типами данных: соединения по полям разных типов (например, Число и Строка) работают крайне медленно.

Соблюдение этих простых правил позволит вашим конфигурациям работать стабильно и быстро даже при существенном росте объема данных. Помните, что оптимизация — это процесс постоянного мониторинга и анализа, поэтому рекомендуем регулярно использовать Консоль запросов с замером времени выполнения для выявления "узких мест".

← На главную