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

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

Как оптимизировать запросы в 1С

Практические методы оптимизации запросов 1С: индексы, виртуальные таблицы, пакетные запросы, типичные ошибки и способы проверки результата.

Медленные запросы — самая частая причина «тормозов» в 1С: отчёт формируется минутами, проведение документа занимает десятки секунд, пользователи жалуются на зависания. В большинстве случаев проблема находится не в сервере, а в самом тексте запроса: лишние соединения, отсутствие отборов по индексированным полям, обращения к виртуальным таблицам без параметров.

Оптимизация запроса в 1С — это работа на трёх уровнях: текст запроса, структура метаданных (индексы) и настройки СУБД. Начинать нужно всегда с текста запроса: он даёт эффект быстрее всего и не требует остановки системы. Ниже разобраны конкретные приёмы, которые применимы в типовых и самописных конфигурациях.

Прежде чем что-то менять, зафиксируйте исходную точку: замерьте время выполнения проблемного запроса на копии базы. Без замера «до» вы не сможете доказать, что оптимизация сработала, и не заметите, если изменение наоборот ухудшило ситуацию.

Как найти проблемный запрос и замерить его скорость

Не оптимизируйте «на глаз». Сначала определите, какой именно запрос тормозит. Для этого есть несколько инструментов: технологический журнал (ТЖ) с событиями DBMSSQL/DBPOSTGRS, консоль запросов с замером времени, а на стороне СУБД — профилировщик SQL Server (Extended Events) или pg_stat_statements для PostgreSQL.

В технологическом журнале настройте фильтр на длительность, например события дольше 2–5 секунд. В тексте события будет виден SQL-запрос и его продолжительность. Сопоставить SQL-запрос с запросом на встроенном языке помогает контекст — имя процедуры или модуля, если включён соответствующий отбор.

Для отдельного запроса в коде используйте замер через ТекущаяУниверсальнаяДатаВМиллисекундах() до и после выполнения. Это грубый, но быстрый способ сравнить варианты запроса между собой на одинаковых данных.

Порядок поиска узкого места
  1. 1
    Включите технологический журнал с фильтром по длительным событиям СУБД
  2. 2
    Воспроизведите проблемную операцию и соберите лог
  3. 3
    Найдите самый долгий запрос и сопоставьте его с кодом конфигурации
  4. 4
    Снимите план запроса в консоли СУБД или через консоль запросов
  5. 5
    Замерьте время до изменений и зафиксируйте его как базовую точку

Как читать план запроса и что в нём искать

План запроса показывает, как СУБД фактически выполняет запрос: какие индексы использует, в каком порядке соединяет таблицы, сканирует ли таблицу целиком. Получить план можно в SQL Server Management Studio (Estimated/Actual Execution Plan) или через вывод плана в консоли запросов на управляемых формах.

Главные тревожные признаки в плане: Clustered Index Scan или Table Scan по большой таблице (чтение всей таблицы вместо поиска по индексу), операции Sort и Hash Match с большим числом строк, а также предупреждения о нехватке памяти и spill в tempdb. Если вы видите сканирование таблицы движений регистра на миллионы записей — это почти наверняка причина медленной работы.

Обращайте внимание на оценку количества строк: если СУБД ожидает 100 строк, а фактически обрабатывает миллион, значит статистика устарела или запрос построен так, что оптимизатор не может правильно оценить селективность. Обновление статистики на СУБД в таком случае иногда даёт эффект без изменения запроса.

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

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

Операции доступа к данным
Норма

Index Seek по индексированным полям

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

Table Scan / Clustered Index Scan по большой таблице

Оценка строк
Норма

Оценка близка к фактическому числу строк

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

Оценка расходится с фактом на порядки

Сортировки и соединения
Норма

Небольшое число строк в Sort и Hash Match

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

Spill в tempdb, миллионы строк в промежуточных операциях

Типовые ошибки в тексте запроса и как их исправить

Соединения с виртуальными таблицами без параметров

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

Функции и выражения в условиях соединения

Если в условии соединения или отбора поле обёрнуто в функцию — например, ГОД(Дата) = 2026 или ПОДСТРОКА(Наименование, 1, 5) — индекс по этому полю использоваться не будет. Заменяйте такие условия на диапазоны: вместо сравнения года используйте Дата МЕЖДУ &НачалоГода И &КонецГода.

Выборка лишних полей и «ВЫБРАТЬ *»

Чем больше полей выбирает запрос, тем больше данных читается с диска и передаётся по сети. Убирайте поля, которые не используются в дальнейшей обработке, особенно поля неограниченной длины (строки, ХранилищеЗначения). Разворачивание ссылок через точку («Товары.Номенклатура.Артикул») тоже порождает скрытые соединения — выбирайте реквизиты явно и только нужные.

Типичные ошибки в запросах
01
Виртуальная таблица без параметров — читается вся таблица движений регистра
02
Функция над полем в условии — индекс не используется, выполняется полное сканирование
03
Соединение по полям разных типов — неявное преобразование типов блокирует индекс
04
Обращение к реквизитам через точку от ссылки — лишние скрытые соединения
05
ИТОГИ по большим выборкам там, где достаточно группировки — лишняя сортировка и память

Как индексы влияют на скорость и когда их добавлять

1С автоматически создаёт индексы по измерениям регистров, ключам справочников и полям с признаком «Индексировать». Если запрос фильтрует по реквизиту без индекса, СУБД вынуждена сканировать таблицу. Проверьте в конфигураторе свойства реквизита: для часто используемых в отборах полей включите индексирование.

Для составных условий важен порядок полей в индексе: индекс работает, когда в отборе участвуют поля с начала списка. Если отбор идёт по Складу и Номенклатуре, а индекс построен как (Номенклатура, Склад), эффект будет слабым. В типовых регистрах порядок измерений менять рискованно — это влияет на всю логику, поэтому сначала пробуйте перестроить запрос под существующий индекс.

Помните и об обратной стороне: каждый дополнительный индекс замедляет запись (проведение документов) и занимает место. Не добавляйте индексы «про запас» — только под подтверждённый замером запрос.

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

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

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

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

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

Соединения, подзапросы и УСЛОВИЕ В: что выбрать

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

Условие В ИЕРАРХИИ(&Группа) по справочнику — отдельный случай: оно порождает рекурсивную логику на уровне платформы. Если отбор по иерархии выполняется внутри большого запроса многократно, выгоднее заранее вычислить список элементов группы во временную таблицу и соединяться с ней.

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

Способы фильтрации данных

Соединение с временной таблицей

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

Вложенный запрос в В(...)

Подходит для небольших выборок и простых условий. На больших данных может выполняться неэффективно

Параметры виртуальных таблиц

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

Уровень СУБД и платформы: что ещё проверить

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

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

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

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

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

Запрос медленный и в консоли, и в работеАнализируйте план запроса и правьте текст: параметры виртуальных таблиц, индексы, лишние поля
Запрос быстрый в консоли, но медленный у пользователейИщите блокировки и ожидания в технологическом журнале, а не ошибки в тексте запроса
План показывает неверные оценки строкОбновите статистику на СУБД и проверьте фрагментацию индексов
Тормозит только после роста объёма данныхПроверьте итоги регистров, рассмотрите свёртку базы и пересмотр отборов по периодам
Итог

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

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

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

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

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

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

План запроса зависит от объёма данных, статистики и фрагментации индексов. На копии базы со свежей статистикой запрос может летать, а на рабочей — сканировать таблицы. Поэтому оптимизацию проверяйте на данных, максимально близких к рабочим, и поддерживайте регламент обновления статистики.

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

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

Влияет ли отключение итогов на скорость запросов к регистрам?

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

Как понять, что проблема в блокировках, а не в самом запросе?

Признак: запрос быстро выполняется в консоли, но «висит» при работе пользователей, причём время растёт при увеличении нагрузки. Подтверждение ищите в технологическом журнале по событиям TLOCK и TDEADLOCK, а на СУБД — по ожиданиям на блокировках. Лечится сокращением транзакций, управляемым режимом блокировок и порядком обращения к таблицам.

Что даёт конструкция «ДЛЯ ИЗМЕНЕНИЯ» и когда её применять?

Она устанавливает управляемую блокировку на читаемые данные до конца транзакции, защищая от конкурентного изменения. Применяйте только внутри транзакции и только когда данные действительно будут меняться: избыточное использование «ДЛЯ ИЗМЕНЕНИЯ» усиливает конкуренцию за блокировки и замедляет параллельную работу.