К статье
Практический материал

Разработка и Конфигурация · 14 мин

Зачем индексировать в запросе 1С и как это делать правильно

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

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

Типичная ситуация: вы помещаете в менеджер временных таблиц выборку на десятки тысяч строк, а затем соединяете её с регистром или другой временной таблицей. Если поле соединения не проиндексировано, сервер баз данных будет перебирать строки полным сканированием. На таблице в сотни строк разница незаметна, на таблице в сотни тысяч строк запрос может выполняться в десятки раз дольше.

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

Что делает конструкция ИНДЕКСИРОВАТЬ ПО

Когда запрос содержит предложение ПОМЕСТИТЬ, результат выборки сохраняется во временной таблице внутри менеджера временных таблиц. По умолчанию такая таблица не имеет никаких индексов — это просто набор строк. Если дальше по ней выполняется соединение или отбор, СУБД просматривает все строки последовательно.

Предложение ИНДЕКСИРОВАТЬ ПО ИмяПоля, записанное после ПОМЕСТИТЬ ИмяТаблицы, заставляет СУБД построить по указанным полям индекс. После этого поиск строк по значению поля выполняется по индексу — логарифмически, а не полным перебором. Можно указать несколько полей через запятую, тогда создаётся составной индекс.

Важно понимать: индекс существует только в рамках пакета запроса и живёт вместе с временной таблицей. После уничтожения таблицы (явно через УНИЧТОЖИТЬ или при закрытии менеджера временных таблиц) индекс исчезает. На постоянные таблицы базы данных — справочники, регистры, документы — эта конструкция не действует: их индексы определяются в конфигураторе свойствами «Индексировать» и «Индексировать с дополнительным упорядочиванием».

Когда индексирование даёт реальный выигрыш

Главный сценарий — соединение временной таблицы с другой таблицей по определённому полю. Например, вы отобрали список контрагентов во временную таблицу, а затем соединяете её с регистром накопления по полю «Контрагент». Если поле проиндексировано, СУБД быстро находит соответствия; если нет — сканирует временную таблицу для каждой строки регистра.

Второй сценарий — соединение двух временных таблиц между собой. Здесь индексировать стоит как минимум ту таблицу, которая окажется «внутренней» в плане запроса, но на практике проще индексировать обе по полям соединения, если обе крупные. Третий сценарий — повторные обращения к одной и той же временной таблице в нескольких последующих запросах пакета: индекс создаётся один раз, а используется многократно.

Также индекс помогает, когда по временной таблице выполняется группировка или отбор с условием по конкретному полю в следующем запросе пакета. Хотя выигрыш здесь обычно скромнее, чем при соединениях, на больших объёмах он всё равно заметен.

Размер временной таблицыТысячи строк и больше

Чем больше строк, тем сильнее выигрыш от индекса при соединениях

Роль таблицыУчаствует в соединении

Поле соединения — первый кандидат на индексирование

Число обращенийТаблица читается несколько раз

Один индекс окупается многократными обращениями

План запросаПолное сканирование временной таблицы

В плане СУБД видно Scan вместо Seek — признак отсутствия индекса

Когда индексировать не нужно

Если временная таблица маленькая — десятки или сотни строк — полное сканирование выполняется мгновенно, а создание индекса добавляет лишнюю работу. На таких объёмах ИНДЕКСИРОВАТЬ ПО не ускорит запрос, а иногда и замедлит его за счёт накладных расходов на построение индекса.

Не имеет смысла индексировать поля, которые не участвуют в соединениях, отборах и группировках последующих запросов. Индекс «про запас» только расходует память и время. Также бессмысленно индексировать временную таблицу, которая используется ровно один раз и только для полного перебора всех её строк — например, для выгрузки в таблицу значений.

Отдельная ошибка — попытка применить ИНДЕКСИРОВАТЬ ПО к обычным таблицам базы данных. Конструкция синтаксически допустима только с ПОМЕСТИТЬ. Для ускорения запросов к постоянным таблицам нужно настраивать индексы в конфигураторе или пересматривать сам запрос.

Сравнение подходов

Индексировать временную таблицу

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

Индексировать реквизит в конфигураторе

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

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

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

Как написать запрос с индексированием: синтаксис и пример

Синтаксис прост: предложение ИНДЕКСИРОВАТЬ ПО ставится сразу после ПОМЕСТИТЬ ИмяТаблицы, до следующего запроса пакета. Поля перечисляются через запятую. Порядок полей в составном индексе имеет значение: индекс эффективен, когда в условии соединения участвуют поля, начиная с первого.

ВЫБРАТЬ

Продажи.Контрагент КАК Контрагент,

СУММА(Продажи.Сумма) КАК Сумма

ПОМЕСТИТЬ ИтогиПоКонтрагентам

ИЗ

РегистрНакопления.Продажи КАК Продажи

ГДЕ

Продажи.Период МЕЖДУ &НачалоПериода И &КонецПериода

СГРУППИРОВАТЬ ПО

Продажи.Контрагент

ИНДЕКСИРОВАТЬ ПО

Контрагент

;

ВЫБРАТЬ

Итоги.Контрагент,

Итоги.Сумма,

Контрагенты.ОсновнойМенеджер

ИЗ

ИтогиПоКонтрагентам КАК Итоги

ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты

ПО Итоги.Контрагент = Контрагенты.Ссылка

В этом примере соединение идёт по полю «Контрагент», поэтому именно оно индексируется. Если бы дальше было соединение по двум полям — например, по контрагенту и договору, — оба поля следовало бы включить в индекс в том же порядке, в каком они используются в условии.

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

Порядок добавления индекса в запрос
  1. 1
    Определите временные таблицы пакета и найдите те, что участвуют в соединениях или отборах последующих запросов
  2. 2
    Выделите поля, по которым выполняются соединения и условия
  3. 3
    Добавьте ИНДЕКСИРОВАТЬ ПО с этими полями сразу после ПОМЕСТИТЬ
  4. 4
    Проверьте, что имена полей совпадают с псевдонимами во временной таблице
  5. 5
    Замерьте время выполнения запроса до и после изменения на реальных объёмах данных

Как проверить, что индекс работает

Самый надёжный способ — замер времени выполнения на реальных или приближенных к реальным данных. Запустите пакет запроса до добавления индекса и после, зафиксируйте длительность. Если таблица крупная и соединение построено по проиндексированному полю, разница обычно хорошо заметна.

Для клиент-серверной базы полезно посмотреть план запроса на стороне СУБД. В MS SQL Server это делается через SQL Server Management Studio (фактический план выполнения), в PostgreSQL — через EXPLAIN ANALYZE. Признак эффективного индекса — операции Index Seek вместо Scan для временной таблицы и снижение стоимости соединения.

Также можно использовать технологический журнал платформы с включёнными событиями DBMSSQL или DBPOSTGRS: по длительности отдельных операторов видно, какая часть пакета тормозит. Если после добавления индекса время не изменилось, значит, либо таблица маленькая, либо соединение идёт не по тем полям, либо проблема вообще в другом месте запроса.

Контрольные точки

Как проверить результат

Время выполнения пакета запроса
Норма

Заметно сократилось на больших данных

Тревожный признак

Не изменилось или выросло — индекс лишний или не тот

План запроса в СУБД
Норма

Index Seek по временной таблице

Тревожный признак

Table Scan на крупной временной таблице

Поля индекса
Норма

Совпадают с полями соединения, порядок как в условии

Тревожный признак

Индексированы поля, не участвующие в соединениях

Типичные ошибки и ограничения

Самая частая ошибка — индексировать «на всякий случай» все временные таблицы по всем полям. Это увеличивает расход памяти и время создания таблиц, а выигрыша не даёт, потому что лишние индексы просто не используются. Индексировать нужно осознанно, исходя из того, как таблица читается дальше.

Вторая ошибка — неверный порядок полей в составном индексе. Если соединение идёт по полям А и Б, а индекс построен как (Б, А), СУБД может не использовать его эффективно. Порядок полей в индексе должен соответствовать порядку их появления в условии соединения.

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

Наконец, помните об ограничении: индексы временных таблиц не сохраняются между вызовами. Каждое выполнение пакета создаёт индексы заново. Если один и тот же большой справочный срез используется во многих запросах системы, подумайте, не должен ли он стать постоянной таблицей (например, регистром сведений) с индексами в конфигураторе.

Типичные ошибки
01
Индексирование всех полей подряд — лишний расход памяти и времени без выигрыша
02
Неверный порядок полей в составном индексе — СУБД не использует индекс
03
Соединение по вычисляемому выражению — индекс по исходному полю не срабатывает
04
Индексирование маленькой таблицы — накладные расходы превышают выигрыш
05
Попытка применить ИНДЕКСИРОВАТЬ ПО к постоянной таблице — конструкция работает только с ПОМЕСТИТЬ

Индексы временных таблиц и индексы в конфигураторе: не путать

В конфигураторе у реквизитов и измерений есть свойство «Индексировать». Оно управляет индексами постоянных таблиц базы данных и влияет на все запросы к ним, но замедляет запись, потому что индекс обновляется при каждом изменении данных. Это принципиально другой механизм, чем ИНДЕКСИРОВАТЬ ПО.

Практическое правило: если узкое место — соединение с постоянной таблицей по неиндексированному реквизиту, сначала попробуйте решить проблему на уровне запроса (временная таблица с нужными полями и индексом). Изменение индексов в конфигураторе — более тяжёлое решение, влияющее на всю систему, и применять его стоит только при систематической потребности в таком отборе.

При работе с регистрами помните, что платформа автоматически строит индексы по измерениям и некоторым стандартным полям. Поэтому соединения по измерениям регистра обычно уже быстрые, а вот соединения по реквизитам и ресурсам — нет. Именно там временная таблица с индексом чаще всего спасает ситуацию.

Когда проблему не решить индексом

Если запрос медленный из-за неявного преобразования типов в условии соединения (например, соединение строки с числом или сравнение полей разной длины), индекс не поможет — СУБД не сможет его использовать. Сначала приведите типы к единому виду через ВЫРАЗИТЬ в самой временной таблице.

Не спасёт индекс и при соединении по условию «не равно» или по диапазону с широким охватом: такие условия по своей природе требуют перебора. Также индекс бессилен, когда временная таблица содержит поля составного типа и соединение идёт по ним — сравнение составных типов само по себе дорогое. Лечится это сужением типа поля через ВЫРАЗИТЬ ещё на этапе формирования временной таблицы.

Если после добавления правильных индексов запрос всё равно медленный, смотрите глубже: объём выбираемых из регистра данных, отсутствие отборов по периоду, соединения с виртуальными таблицами без параметров, вложенные запросы в условиях. Индекс временной таблицы — один из инструментов оптимизации, но не единственный.

Маршрут решения

Как выбрать действие

Временная таблица крупная и соединяется с другой таблицейДобавьте ИНДЕКСИРОВАТЬ ПО по полям соединения и замерьте время
Соединение идёт по вычисляемому выражению или составному типуМатериализуйте значение через ВЫРАЗИТЬ во временной таблице и индексируйте его
Запрос медленный, но временных таблиц нетАнализируйте план запроса и индексы постоянных таблиц, а не добавляйте ПОМЕСТИТЬ без необходимости
Один и тот же срез данных нужен многим запросам системыРассмотрите постоянную таблицу (регистр сведений) с индексами в конфигураторе

Частые вопросы

Работает ли ИНДЕКСИРОВАТЬ ПО в файловой базе 1С?

Да, конструкция доступна независимо от режима работы базы. Однако в файловом варианте выигрыш обычно менее выражен, потому что встроенный движок базы данных иначе обрабатывает временные таблицы. Максимальный эффект индексирование даёт в клиент-серверном режиме на MS SQL Server, PostgreSQL или Oracle, где индекс реально используется оптимизатором СУБД.

Нужно ли индексировать временную таблицу из нескольких строк?

Нет. На таблицах из десятков или сотен строк полное сканирование выполняется мгновенно, а создание индекса добавляет лишнюю работу. Порог, после которого индекс начинает окупаться, зависит от запроса и СУБД, но ориентировочно это тысячи строк и больше при соединениях.

Можно ли проиндексировать временную таблицу по нескольким полям?

Да, поля перечисляются через запятую: ИНДЕКСИРОВАТЬ ПО Поле1, Поле2. Создаётся составной индекс, и порядок полей важен: он должен соответствовать порядку полей в условии соединения или отбора, иначе СУБД может не использовать индекс эффективно.

Почему после добавления индекса запрос не стал быстрее?

Типичные причины: таблица слишком мала; соединение идёт не по проиндексированным полям или в другом порядке; в условии соединения поле обёрнуто в функцию; поле имеет составной тип. Проверьте план запроса на стороне СУБД — если там осталось полное сканирование временной таблицы, индекс по какой-то причине не используется.

Чем ИНДЕКСИРОВАТЬ ПО отличается от свойства «Индексировать» в конфигураторе?

ИНДЕКСИРОВАТЬ ПО создаёт временный индекс для временной таблицы внутри одного пакета запроса и ничего не меняет в базе. Свойство «Индексировать» в конфигураторе создаёт постоянный индекс таблицы базы данных: он ускоряет все запросы к этой таблице, но замедляет запись и требует реструктуризации.

Нужно ли уничтожать индексированные временные таблицы?

Уничтожать временные таблицы через УНИЧТОЖИТЬ рекомендуется, когда они больше не нужны в пакете: это освобождает память и ресурсы СУБД. Индекс при этом удаляется автоматически вместе с таблицей, отдельных действий для него не требуется.

Итог

Что важно запомнить

ИНДЕКСИРОВАТЬ ПО ускоряет соединения и отборы по крупным временным таблицам внутри пакета запроса. Индексируйте только поля соединений и отборов, соблюдайте порядок полей в составном индексе и проверяйте эффект замером времени или планом запроса. Для постоянных таблиц используются индексы конфигуратора — это отдельный механизм.