Разработка и Конфигурация · 11 мин
Как индексировать в запросе 1С: практическое руководство
Как индексировать временные таблицы в запросе 1С: синтаксис ИНДЕКСИРОВАТЬ ПО, когда индекс ускоряет запрос, типичные ошибки и проверка результата.
Конструкция ИНДЕКСИРОВАТЬ ПО в языке запросов 1С создаёт индекс для временной таблицы, помещённой во временное хранилище менеджера временных таблиц. Индекс ускоряет соединения и условия отбора по проиндексированным полям в последующих запросах пакета, но не влияет на таблицы базы данных — их индексы создаются только в конфигураторе.
Механизм работает так: первый запрос пакета выбирает данные и помещает их во временную таблицу с указанием индексируемых полей, а второй и последующие запросы соединяются с этой таблицей или фильтруются по ней. Без индекса платформа сканирует временную таблицу полностью при каждом обращении, что на больших объёмах приводит к заметным задержкам.
Важно понимать границы применимости: индексирование оправдано только для временных таблиц с существенным количеством строк, которые используются в соединениях или условиях дальше по пакету. На маленьких выборках накладные расходы на построение индекса могут превысить выигрыш.
Где именно в запросе 1С применяется индексирование
Конструкция ИНДЕКСИРОВАТЬ ПО допустима только в запросах, которые создают временную таблицу, то есть содержат предложение ПОМЕСТИТЬ. Индекс строится по указанным полям в момент выполнения запроса и живёт вместе с временной таблицей до уничтожения менеджера временных таблиц или явного удаления таблицы запросом УНИЧТОЖИТЬ.
Типичный сценарий — пакетный запрос из двух и более частей: сначала отбираются данные в ВТ_Данные, затем она соединяется с регистрами или другими временными таблицами. Если соединение идёт по полю, которое не входит в индекс, платформа выполнит полный перебор строк временной таблицы.
К таблицам базы данных — справочникам, документам, регистрам — ИНДЕКСИРОВАТЬ ПО неприменимо. Их индексы задаются в конфигураторе: свойство «Индексировать» у реквизита, индексы измерений регистров, автоматические индексы по ссылкам и стандартным реквизитам.
Синтаксис ИНДЕКСИРОВАТЬ ПО с примером
Секция ИНДЕКСИРОВАТЬ ПО располагается после ПОМЕСТИТЬ и перечисляет одно или несколько полей через запятую. Порядок секций в запросе фиксирован: выборка, ПОМЕСТИТЬ, ИНДЕКСИРОВАТЬ ПО, УПОРЯДОЧИТЬ ПО (если нужна сортировка).
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
СУММА(Продажи.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
ВЫБРАТЬ
Товары.Ссылка,
ВТ_Продажи.Количество
ИЗ
Справочник.Номенклатура КАК Товары
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Продажи КАК ВТ_Продажи
ПО Товары.Ссылка = ВТ_Продажи.Номенклатура
Здесь индекс по полю Номенклатура позволяет второму запросу быстро находить строки временной таблицы при соединении, вместо полного перебора. В качестве индексируемого указывается псевдоним поля из списка выборки, а не исходное имя реквизита.
ВЫБРАТЬ Т.Контрагент, Т.Сумма ПОМЕСТИТЬ ВТ_Данные ИЗ ... ИНДЕКСИРОВАТЬ ПО КонтрагентКакие поля стоит индексировать
Индексировать нужно те поля, которые реально участвуют в условиях поиска по временной таблице: поля соединения (ПО Т1.Поле = Т2.Поле), поля из условий ГДЕ и В при обращении к временной таблице, поля вложенных запросов, фильтрующих выборку.
Если соединение идёт по нескольким полям одновременно, имеет смысл включить в индекс все эти поля в том же порядке, в котором они используются в условии. Составной индекс эффективно работает при фильтрации по префиксу полей: индекс (Организация, Контрагент) поможет при условии по обоим полям и по одной организации, но не при условии только по контрагенту.
Не индексируйте поля «про запас»: каждый лишний индекс — это время на его построение и расход памяти. Поля с низкой селективностью (например, булево или признак с двумя значениями) в качестве единственного индекса почти не дают выигрыша.
Как выбрать поля для индекса
Когда индекс реально ускоряет, а когда вредит
Выигрыш от индекса проявляется на временных таблицах со значительным количеством строк — от тысяч и выше, когда соединение или отбор выполняется многократно или по большому набору ключей. Чем больше таблица и чем чаще к ней обращаются по ключу, тем заметнее эффект.
На маленьких таблицах — десятки или сотни строк — построение индекса занимает время, а поиск и так выполняется быстро полным перебором. В этом случае ИНДЕКСИРОВАТЬ ПО замедляет выполнение пакета, хотя и незначительно.
Ещё одна ситуация, где индекс бесполезен: временная таблица создаётся и сразу выбирается целиком в конечный результат без соединений и фильтров. Индекс здесь не будет использован ни разу.
Индексы таблиц базы данных: что делается в конфигураторе
Часто под «индексированием в запросе» подразумевают другую задачу: запрос к регистру или справочнику работает медленно, и нужно ускорить выборку на уровне СУБД. Здесь запрос бессилен — индексы физических таблиц создаются в конфигураторе.
Основные инструменты: свойство «Индексировать» у реквизита объекта (варианты «Индексировать» и «Индексировать с доп. упорядочиванием»), автоматическое индексирование измерений регистров, входящих в отборы виртуальных таблиц, индексы по ссылке, дате и номеру, которые платформа создаёт сама.
Практическое правило: реквизит стоит индексировать, если по нему регулярно выполняется поиск, отбор или соединение в типовых операциях, и таблица достаточно велика. Массовое включение индексации у всех реквизитов увеличивает размер базы и замедляет запись, поэтому решение принимают по реальным узким местам, выявленным замером производительности.
Как проверить, что индекс используется
Прямого инструмента «показать план запроса» для временных таблиц в типовой консоли нет, поэтому проверка строится на замерах: выполните пакетный запрос с ИНДЕКСИРОВАТЬ ПО и без него на реальных объёмах данных и сравните время выполнения. Разница должна быть устойчивой при повторных запусках, а не случайной флуктуацией.
Для запросов к таблицам базы данных используйте технологический журнал с событиями СУБД и планы запросов на стороне MS SQL Server или PostgreSQL: там видно, идёт поиск по индексу (Index Seek) или полное сканирование (Seq Scan / Clustered Index Scan).
Признак того, что индекс не работает: после добавления ИНДЕКСИРОВАТЬ ПО время не изменилось, а в плане СУБД по-прежнему сканирование. Обычно это значит, что поле индекса не совпадает с полем соединения, типы данных в условии различаются или таблица слишком мала.
Как проверить результат
Заметно сократилось на реальных объёмах
Не изменилось или выросло — индекс не используется или лишний
Совпадает с полем соединения или условия
Индекс по одному полю, а соединение — по другому
Index Seek по нужному индексу
Полное сканирование таблицы
Типичные ошибки при индексировании
Самая частая ошибка — индексировать поле, которое не участвует в соединении или отборе. Например, временная таблица индексируется по сумме, а соединяется по номенклатуре: индекс построен, но платформа его не использует.
Вторая ошибка — несовпадение типов в условии соединения: в одной таблице поле строкового типа одной длины, в другой — другой, либо сравнивается ссылка с представлением. Приведение типов в условии может отключить использование индекса.
Третья группа ошибок — ожидание эффекта там, где его не может быть: индексирование мелкой временной таблицы, попытка применить ИНДЕКСИРОВАТЬ ПО к таблице базы данных, надежда, что индекс в запросе исправит отсутствие индекса в конфигураторе.
Практический порядок оптимизации медленного запроса
Действуйте от замера, а не от предположений. Сначала выясните, какая часть пакетного запроса занимает время: создание временных таблиц или финальная выборка с соединениями. Затем проверьте, есть ли индексы по полям соединений — во временных таблицах через ИНДЕКСИРОВАТЬ ПО, в таблицах базы данных через конфигуратор.
Только после этого имеет смысл переписывать сам запрос: убирать лишние соединения, заменять В с перечнем на соединение с временной таблицей, сокращать состав выбираемых полей.
- 1Замерьте время выполнения запроса на реальных данных
- 2Определите, какие временные таблицы участвуют в соединениях и отборах
- 3Добавьте ИНДЕКСИРОВАТЬ ПО по полям соединений и условий
- 4Проверьте типы полей в условиях соединения — они должны совпадать
- 5Повторите замер и сравните результат
- 6Если узкое место в таблицах БД — проанализируйте индексы в конфигураторе и план запроса на СУБД
Если запрос многократно соединяется с одной и той же временной таблицей, выгоднее один раз построить по ней индекс, чем фильтровать её условием В (ВЫБРАТЬ ...) — вложенный запрос в условии часто выполняется без использования индекса.
Что важно запомнить
ИНДЕКСИРОВАТЬ ПО применяется только к временным таблицам и только по полям соединений и отборов. Эффект проверяется замером на реальных объёмах. Индексы таблиц базы данных настраиваются в конфигураторе, а их использование — через план запроса на СУБД.
Частые вопросы
Можно ли проиндексировать таблицу значений, переданную в запрос как параметр?
Да, но не напрямую. Таблица значений передаётся параметром, помещается во временную таблицу запросом с ПОМЕСТИТЬ, и уже в этом запросе указывается ИНДЕКСИРОВАТЬ ПО по нужным полям. Дальнейшие соединения с этой временной таблицей будут использовать индекс.
Работает ли ИНДЕКСИРОВАТЬ ПО без ПОМЕСТИТЬ?
Нет. Конструкция синтаксически допустима только в запросах, создающих временную таблицу. В обычном запросе выборки она бессмысленна, поскольку результат сразу возвращается и индексировать нечего.
Сколько полей можно указать в ИНДЕКСИРОВАТЬ ПО?
Можно перечислить несколько полей через запятую — будет построен составной индекс. Практический предел определяется здравым смыслом: каждое дополнительное поле увеличивает время построения индекса и расход памяти, поэтому включайте только поля, реально участвующие в поиске.
Почему запрос с индексом всё равно медленный?
Частые причины: индекс построен не по тому полю, типы данных в условии соединения различаются, временная таблица мала и узкое место в другом, либо основное время уходит на выборку из таблиц базы данных, где нужен индекс уровня конфигуратора. Проверяйте замером по частям пакета.
Чем ИНДЕКСИРОВАТЬ ПО отличается от свойства «Индексировать» у реквизита?
ИНДЕКСИРОВАТЬ ПО создаёт временный индекс в памяти на время жизни менеджера временных таблиц и управляется из текста запроса. Свойство «Индексировать» у реквизита создаёт постоянный индекс в базе данных и настраивается в конфигураторе с последующей реструктуризацией.
Нужно ли удалять временные таблицы после использования?
Желательно. Запрос УНИЧТОЖИТЬ ВТ_Данные освобождает память, занятую таблицей и её индексами, что важно в длительных пакетах с несколькими крупными временными таблицами. Если таблицы не удалять, они живут до закрытия менеджера временных таблиц.