Как оптимизировать сложный запрос в 1С и заставить индексы во временных таблицах работать

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

Проблема низкой производительности запросов — одна из самых частых задач, с которыми сталкивается разработчик 1С. Часто бывает так, что простое добавление инструкции ИНДЕКСИРОВАТЬ ПО не приносит ожидаемого результата. Разберем на конкретном примере, почему запрос может выполняться долго (в нашем случае — 6 секунд) даже при наличии 65 000 записей, и как правильно перестроить логику пакета запросов для достижения максимальной скорости.

Анализируем неэффективность соединений

Проанализируем ситуацию: если запрос с левыми соединениями работает медленно, индексирование только основной (левой) таблицы часто оказывается бесполезным. Важно понимать, как SQL-сервер обрабатывает соединение. Для эффективной работы алгоритмов (например, Nested Loops или Merge Join) индексы должны присутствовать в обеих соединяемых таблицах по полям, участвующим в условии ПО.

Рассмотрим рекомендации по шагам:

  1. Проверьте поля присоединяемых таблиц. Если вы присоединяете таблицу Б к таблице А, то поле в таблице Б обязательно должно быть проиндексировано.
  2. Изолируйте проблемное соединение. Метод «исключения» — лучший способ поиска узкого места. Поочередно отключайте левые соединения в финальном запросе и замеряйте время. То соединение, после отключения которого запрос «полетит», и является причиной тормозов.
  3. Следите за состоянием индексов. В продуктивных базах индексы могут деградировать (фрагментироваться). Иногда помогает простое обновление статистик или реорганизация индексов на стороне СУБД.

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

Одной из самых грубых ошибок в представленном запросе является использование виртуальной таблицы СрезПоследних непосредственно в левом соединении финального запроса:


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

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

Правильное решение: всегда выносите срез последних в отдельную временную таблицу, индексируйте её по полю связи и только потом используйте в основном соединении.


// Сначала подготавливаем данные
ВЫБРАТЬ
    Скидки.Объект КАК Объект,
    Скидки.ДисконтнаяСхема КАК ДисконтнаяСхема
ПОМЕСТИТЬ втСкидкиСрез
ИЗ
    РегистрСведений.СкидкиНаОбъекты.СрезПоследних(&Дата, СтруктурнаяЕдиница В (&СписокСЕ)) КАК Скидки
ИНДЕКСИРОВАТЬ ПО
    Объект;

Оптимизация условий ГДЕ и подзапросов

Посмотрим на конструкцию в тексте запроса, которую опытные разработчики называют «безобразием». Это использование вложенных запросов внутри оператора ГДЕ ... В (...). Такие конструкции заставляют систему выполнять вложенную выборку многократно или порождают тяжелые полусоединения (Semi-Join).

Рассмотрим подробнее фрагмент:


ГДЕ Номенклатура.Ссылка В (ВЫБРАТЬ Номенклатура.Ссылка ИЗ Справочник.Номенклатура ...)

Для оптимизации этого узла следует применить следующий алгоритм:

Проблема разыменования ссылок (обращение через точку)

Проанализируем ситуацию с полями втСрезРА.Номенклатура.Родитель и втСрезРА.СтруктурнаяЕдиница.Наименование. В языке запросов 1С каждое обращение через точку к ссылочному полю — это неявное ЛЕВОЕ СОЕДИНЕНИЕ с соответствующей таблицей справочника.

Если таких обращений много, и они происходят в итоговом запросе, количество соединений в итоговом SQL-запросе может вырасти в геометрической прогрессии. Выясним причину: если в втСрезРА у нас 65 000 строк, то для каждой строки система будет пытаться «дотянуться» до родителя номенклатуры. Правильнее будет выбрать Родитель и Наименование еще на этапе формирования временной таблицы втСрезРА.

Селективность индекса и статистика

Выясним, почему индексирование поля ЭтоОтмена или ЭтоГруппа обычно не дает эффекта. Это связано с понятием селективности. Если поле имеет всего два значения (Истина/Ложь), индекс по нему практически бесполезен, так как он не позволяет серверу быстро отсечь большую часть записей. SQL-сервер скорее выберет Table Scan (полное сканирование), чем будет тратить ресурсы на чтение индекса.

Однако индексирование временных таблиц в 1С имеет еще один важный побочный эффект: в момент создания индекса система обновляет статистику этой таблицы. Это критически важно для оптимизатора SQL. Без статистики сервер «не видит» объема данных во временной таблице и может выбрать неверный алгоритм соединения (например, Nested Loops вместо Hash Join), что фатально замедляет работу на больших объемах данных.

Отказ от Автоупорядочивания

В итоговом запросе часто встречается инструкция УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ АВТОУПОРЯДОЧИВАНИЕ. Важно знать, что АВТОУПОРЯДОЧИВАНИЕ — это крайне ресурсоемкая операция. Она заставляет систему добавлять в выборку дополнительные поля (код, наименование, порядок иерархии) и выполнять сортировку по ним. На больших объемах данных это создает огромную нагрузку на дисковую подсистему и процессор. Если строгий порядок по коду/наименованию не критичен для логики, от этой инструкции лучше отказаться.

Итоговый чек-лист для ускорения запроса

  1. Разбейте сложный запрос на этапы. Чем проще каждое отдельное звено пакета, тем легче SQL-серверу построить оптимальный план.
  2. Индексируйте поля связи. Индекс должен быть на полях ПО в обеих таблицах (и в ведущей, и в присоединяемой).
  3. Типизируйте данные. Убедитесь, что вы не соединяете поля составного типа с конкретными ссылками без приведения типов (функция ВЫРАЗИТЬ).
  4. Минимизируйте объем временных таблиц. Используйте внутренние соединения для предварительной фильтрации данных еще до того, как они попадут в финальную выборку.
  5. Используйте профайлер. Если ничего не помогает, воспользуйтесь консолью запросов с замером времени или SQL Profiler, чтобы увидеть реальный план выполнения.

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

← На главную