Индексы в запросах 1С 8.3: временные таблицы, план выполнения и условия, при которых СУБД берёт индекс

· 10 мин

Содержание

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

Запрос, который на копии базы отрабатывает за секунду, на рабочей базе с миллионом строк может висеть минуту. Частая причина — СУБД не нашла подходящего индекса и читает таблицу целиком. Всё описанное относится к платформе 8.3, к файловому и клиент-серверному вариантам.

Какие индексы платформа создаёт без вашего участия

Разработчик 1С не пишет CREATE INDEX. Платформа сама строит индексы при реструктуризации базы, исходя из метаданных. Знать их состав нужно, чтобы понимать, по каким полям отбор будет быстрым «бесплатно».

ОбъектЧто индексируется автоматически
СправочникСсылка; код и наименование, если они заданы в объекте
ДокументСсылка; дата и номер
Табличная частьСсылка на владельца и номер строки
Регистр сведенийИзмерения в порядке их следования в конфигураторе, у периодического — вместе с периодом
Регистр накопленияТаблица движений — по регистратору и номеру строки, а также по периоду; таблица итогов — по измерениям
Регистр бухгалтерииАналогично: движения по регистратору и периоду, итоги по счёту и измерениям

Из таблицы вытекают три практических вывода:

  • 🔑 отбор по ссылке, коду, номеру или регистратору всегда быстрый;
  • 📌 у регистра сведений важен порядок измерений: индекс полезен, когда условие задаёт первое измерение (или первые несколько подряд);
  • ⚠️ реквизит справочника или документа без флажка индексации — это полный просмотр таблицы при каждом отборе по нему.

Полнотекстовый индекс (поиск по словам в списках) к запросам отношения не имеет. Это отдельный механизм, его обновляют регламентным заданием, и на скорость ВЫБРАТЬ … ГДЕ он не влияет.

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

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

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

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    СУММА(Товары.Количество) КАК Количество
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
    Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
    И Товары.Ссылка.Проведен
СГРУППИРОВАТЬ ПО
    Товары.Номенклатура
ИНДЕКСИРОВАТЬ ПО
    Номенклатура
;

////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Продажи.Номенклатура КАК Номенклатура,
    ВТ_Продажи.Количество КАК Продано,
    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
    ВТ_Продажи КАК ВТ_Продажи
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(
                &КонецПериода,
                Номенклатура В
                    (ВЫБРАТЬ
                        ВТ.Номенклатура
                    ИЗ
                        ВТ_Продажи КАК ВТ)) КАК Остатки
        ПО ВТ_Продажи.Номенклатура = Остатки.Номенклатура

Имена документа и регистра взяты для примера: подставьте объекты своей конфигурации.

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

Когда индекс временной таблицы нужен, а когда мешает

СитуацияИндексировать?
Во временной таблице тысячи строк и больше, по полю будет соединение или отборДа
Таблица используется в В (ВЫБРАТЬ … ИЗ ВТ) в параметрах виртуальной таблицыДа, по этому полю
В таблице десятки строкНет: построение индекса дороже выигрыша
Таблица читается один раз целиком, без соединенийНет
Поле соединения составного типаЛучше сначала привести поле через ВЫРАЗИТЬ, затем индексировать

Индексируйте по тем полям и в том порядке, в каком они участвуют в условии ПО следующего запроса. Индекс по полям, которые потом нигде не сравниваются, только удлиняет запрос.

Как прочитать план выполнения запроса 1С

Угадывать, использует СУБД индекс или нет, бессмысленно. Надо смотреть план — то, как СУБД решила выполнить SQL-запрос, в который платформа превратила ваш текст.

Шаг 1. Включить технологический журнал с планами

Технологический журнал настраивают файлом logcfg.xml в каталоге conf платформы на сервере 1С. Минимальная настройка для сбора SQL-запросов и их планов на MS SQL Server:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
    <log location="D:\TechLog" history="2">
        <event>
            <eq property="name" value="dbmssql"/>
        </event>
        <property name="all"/>
    </log>
    <plansql/>
</config>

Сбор планов заметно нагружает сервер и быстро заполняет диск. Включайте журнал на 10–20 минут, на время воспроизведения медленной операции, и сразу удаляйте файл logcfg.xml. Пользователей при этом выгонять не нужно: журнал подхватывается без перезапуска.

Шаг 2. Найти свой запрос

В событиях DBMSSQL есть текст SQL, длительность и план. Ищите по длительности самые тяжёлые события. В тексте вместо привычных имён будут _Reference45, _InfoRg1234, _Fld567.

Чтобы понять, какая это таблица, выполните на сервере функцию, возвращающую соответствие объектов метаданных и таблиц СУБД:

Структура = ПолучитьСтруктуруХраненияБазыДанных(, Истина);

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

Шаг 3. Оценить операции в плане

Операция в планеЧто значитОценка
Index SeekПоиск по индексу, читается только нужный диапазонХорошо
Index Scan / Clustered Index ScanПросмотр всего индексаПлохо на больших таблицах
Table ScanЧтение всей таблицыПлохо
Key LookupДочитывание полей, которых нет в индексеТерпимо, если строк мало
Nested Loops с большим числом строкСоединение «каждый с каждым»Повод искать причину

Для PostgreSQL возьмите текст запроса из журнала (событие DBPOSTGRS) и выполните его в консоли СУБД с EXPLAIN ANALYZE. Вместо Index Seek там будет Index Scan using …, а вместо Table Scan — Seq Scan.

В файловом варианте плана нет. Там проверяют замером: тот же запрос на той же базе с изменённым условием. Если время упало в разы — условие стало использовать индекс.

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

Виртуальные таблицы Остатки, Обороты, ОстаткиИОбороты, СрезПоследних, СрезПервых уже оптимизированы платформой. Остатки на текущую дату читаются из таблицы итогов, а не суммируются по движениям. Но выигрыш сохраняется, только если отбор задан в параметре «Условие», а не в ГДЕ.

Плохо — условие после получения остатков:

ВЫБРАТЬ
    Остатки.Номенклатура,
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки
ГДЕ
    Остатки.Склад = &Склад

Здесь СУБД сначала посчитает остатки по всем складам, а затем отбросит лишнее.

Хорошо — условие в параметре:

ВЫБРАТЬ
    Остатки.Номенклатура,
    Остатки.КоличествоОстаток
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки

Отбор применяется к итогам и движениям до группировки и попадает на индекс по измерениям.

Правила работы с параметрами:

  • ✅ в условии используйте только поля самой виртуальной таблицы: измерения и реквизиты, доступные в её параметрах;
  • ✅ для отбора по списку — Номенклатура В (&Список) или В (ВЫБРАТЬ … ИЗ ВТ) с индексированной временной таблицей;
  • ❌ не соединяйте виртуальную таблицу с другой таблицей, рассчитывая, что соединение само ограничит выборку: остатки сначала посчитаются по всему регистру;
  • ❌ не указывайте пустой период у СрезПоследних и Остатки, если вам нужна конкретная дата: пустое значение означает «на текущий момент».

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

Свойство «Индексировать» в конфигураторе

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

  1. Откройте конфигуратор и найдите объект в дереве конфигурации.
  2. Откройте палитру свойств нужного реквизита или измерения.
  3. В свойстве «Индексирование» выберите Индексировать или Индексировать с доп. упорядочиванием. У измерений регистров это флажок «Индексировать».
  4. Сохраните конфигурацию и выполните Конфигурация → Обновить конфигурацию базы данных (F7).

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

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

ВариантКогда выбирать
Не индексироватьПоле не участвует в отборах и соединениях
ИндексироватьОтбор и соединения по полю в запросах
Индексировать с доп. упорядочиваниемОтбор по полю в динамическом списке с сортировкой по коду или наименованию — в индекс добавляется поле основного представления

Не индексируйте всё подряд. Каждый индекс замедляет запись: при проведении документа СУБД обновляет все индексы затронутых таблиц. Строки неограниченной длины индексировать нельзя вовсе.

В типовой конфигурации на поддержке изменение свойств объекта требует включить возможность изменения. Это усложняет последующие обновления: при каждом релизе придётся сравнивать и объединять изменения. Сначала проверьте, нельзя ли переписать сам запрос.

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

Условия, при которых индекс есть, а СУБД его не берёт

Индекс существует, но план всё равно показывает Scan. Это значит, что условие сформулировано так, что СУБД не может искать по индексу диапазоном.

Как написаноПочему не работаетКак переписать
НАЧАЛОПЕРИОДА(Док.Дата, ДЕНЬ) = &ДеньФункция над полем: индекс по дате не применимДок.Дата МЕЖДУ &НачалоДня И &КонецДня
Спр.Наименование ПОДОБНО "%болт%"Шаблон начинается с %По возможности ПОДОБНО "Болт%" или отбор по коду/артикулу
Т.Склад = &С1 ИЛИ Т.Контрагент = &КИЛИ по разным полям ломает поиск по одному индексуДва запроса через ОБЪЕДИНИТЬ ВСЕ
Т.Регистратор.Дата > &ДРазыменование составного поля: соединение со всеми таблицами документовОтбор по периоду регистра или ВЫРАЗИТЬ(Т.Регистратор КАК Документ.…)
Соединение с подзапросом (ВЫБРАТЬ … СГРУППИРОВАТЬ ПО …)У подзапроса нет индекса, а статистику СУБД оценивает плохоВынести подзапрос во временную таблицу с ИНДЕКСИРОВАТЬ ПО
Сравнение поля со значением другого типаНеявное приведение типов на стороне СУБДПередавать параметр того же типа, что поле

Отдельный случай — ограничение доступа на уровне записей (RLS). Оно дописывает в запрос свои условия, и под пользователем запрос выполняется медленнее, чем под администратором. Проверяйте скорость под тем пользователем, у которого «тормозит», и смотрите план именно его сеанса.

Диагностика: выберите симптом

Ошибки при оптимизации запросов через индексы

  • 🧱 Индекс ради индекса. Флажок «Индексировать» на десятке реквизитов документа замедляет проведение у всех пользователей, а выигрыш получают один-два отчёта.
  • 🔀 Порядок полей в ИНДЕКСИРОВАТЬ ПО не совпадает с условием соединения. Если соединяете по складу и номенклатуре, первым в индексе ставьте то поле, которое есть во всех условиях.
  • 📅 Отбор по Ссылка.Дата в табличной части без ограничения по периоду в регистре. Для отчётов по продажам часто быстрее брать данные из регистра с отбором по периоду, а не из табличных частей документов.
  • 🧮 Запрос в цикле. Сто одинаковых запросов с разными параметрами медленнее одного запроса с В (&Список), даже если каждый попадает в индекс.
  • 🧪 Замер на маленькой копии. На базе в 10 тысяч строк Scan и Seek неотличимы. Проверяйте на копии рабочей базы с реальным объёмом данных.

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

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