Проблема низкой производительности запросов — одна из самых частых задач, с которыми сталкивается разработчик 1С. Часто бывает так, что простое добавление инструкции ИНДЕКСИРОВАТЬ ПО не приносит ожидаемого результата. Разберем на конкретном примере, почему запрос может выполняться долго (в нашем случае — 6 секунд) даже при наличии 65 000 записей, и как правильно перестроить логику пакета запросов для достижения максимальной скорости.
Проанализируем ситуацию: если запрос с левыми соединениями работает медленно, индексирование только основной (левой) таблицы часто оказывается бесполезным. Важно понимать, как SQL-сервер обрабатывает соединение. Для эффективной работы алгоритмов (например, Nested Loops или Merge Join) индексы должны присутствовать в обеих соединяемых таблицах по полям, участвующим в условии ПО.
Рассмотрим рекомендации по шагам:
Б к таблице А, то поле в таблице Б обязательно должно быть проиндексировано.Одной из самых грубых ошибок в представленном запросе является использование виртуальной таблицы СрезПоследних непосредственно в левом соединении финального запроса:
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СкидкиНаОбъекты.СрезПоследних(, СтруктурнаяЕдиница В (&СписокСЕ)) КАК СкидкиНаОбъектыСрезПоследних
ПО (СкидкиНаОбъектыСрезПоследних.Объект = СпрНоменклатура.Ссылка)
Разберем, почему это плохо. Виртуальная таблица СрезПоследних сама по себе является сложным подзапросом, который выполняет поиск максимальных периодов и группировку. Когда SQL-сервер пытается соединить результат этого подзапроса с другой физической или временной таблицей, оптимизатор часто ошибается в выборе плана, что приводит к полному сканированию таблиц.
Правильное решение: всегда выносите срез последних в отдельную временную таблицу, индексируйте её по полю связи и только потом используйте в основном соединении.
// Сначала подготавливаем данные
ВЫБРАТЬ
Скидки.Объект КАК Объект,
Скидки.ДисконтнаяСхема КАК ДисконтнаяСхема
ПОМЕСТИТЬ втСкидкиСрез
ИЗ
РегистрСведений.СкидкиНаОбъекты.СрезПоследних(&Дата, СтруктурнаяЕдиница В (&СписокСЕ)) КАК Скидки
ИНДЕКСИРОВАТЬ ПО
Объект;
Посмотрим на конструкцию в тексте запроса, которую опытные разработчики называют «безобразием». Это использование вложенных запросов внутри оператора ГДЕ ... В (...). Такие конструкции заставляют систему выполнять вложенную выборку многократно или порождают тяжелые полусоединения (Semi-Join).
Рассмотрим подробнее фрагмент:
ГДЕ Номенклатура.Ссылка В (ВЫБРАТЬ Номенклатура.Ссылка ИЗ Справочник.Номенклатура ...)
Для оптимизации этого узла следует применить следующий алгоритм:
ОБЪЕДИНИТЬ для формирования единого списка ссылок во временной таблице.В (ВЫБРАТЬ...) на ВНУТРЕННЕЕ СОЕДИНЕНИЕ с этой временной таблицей.Проанализируем ситуацию с полями втСрезРА.Номенклатура.Родитель и втСрезРА.СтруктурнаяЕдиница.Наименование. В языке запросов 1С каждое обращение через точку к ссылочному полю — это неявное ЛЕВОЕ СОЕДИНЕНИЕ с соответствующей таблицей справочника.
Если таких обращений много, и они происходят в итоговом запросе, количество соединений в итоговом SQL-запросе может вырасти в геометрической прогрессии. Выясним причину: если в втСрезРА у нас 65 000 строк, то для каждой строки система будет пытаться «дотянуться» до родителя номенклатуры. Правильнее будет выбрать Родитель и Наименование еще на этапе формирования временной таблицы втСрезРА.
Выясним, почему индексирование поля ЭтоОтмена или ЭтоГруппа обычно не дает эффекта. Это связано с понятием селективности. Если поле имеет всего два значения (Истина/Ложь), индекс по нему практически бесполезен, так как он не позволяет серверу быстро отсечь большую часть записей. SQL-сервер скорее выберет Table Scan (полное сканирование), чем будет тратить ресурсы на чтение индекса.
Однако индексирование временных таблиц в 1С имеет еще один важный побочный эффект: в момент создания индекса система обновляет статистику этой таблицы. Это критически важно для оптимизатора SQL. Без статистики сервер «не видит» объема данных во временной таблице и может выбрать неверный алгоритм соединения (например, Nested Loops вместо Hash Join), что фатально замедляет работу на больших объемах данных.
В итоговом запросе часто встречается инструкция УПОРЯДОЧИТЬ ПО ... ИЕРАРХИЯ АВТОУПОРЯДОЧИВАНИЕ. Важно знать, что АВТОУПОРЯДОЧИВАНИЕ — это крайне ресурсоемкая операция. Она заставляет систему добавлять в выборку дополнительные поля (код, наименование, порядок иерархии) и выполнять сортировку по ним. На больших объемах данных это создает огромную нагрузку на дисковую подсистему и процессор. Если строгий порядок по коду/наименованию не критичен для логики, от этой инструкции лучше отказаться.
ПО в обеих таблицах (и в ведущей, и в присоединяемой).ВЫРАЗИТЬ).Соблюдение этих правил позволит сократить время выполнения запроса с нескольких секунд до сотых долей секунды, обеспечив стабильную работу системы даже при росте объема данных.