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

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

1С: как ускорить запрос — практическое руководство

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

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

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

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

Как найти, что именно тормозит

Первое правило оптимизации: не менять запрос, пока не измерено, где теряется время. В управляемом приложении используйте замер производительности (Функциональность — Оценка производительности) или консоль запросов с замером времени выполнения. Для серверной базы полезен технологический журнал с событием DBMSSQL или DBPOSTGRS — он покажет, какой именно SQL-запрос и сколько выполнялся.

Обратите внимание на разницу между временем выполнения запроса на сервере СУБД и общим временем операции. Если запрос отрабатывает за 200 мс, а отчёт строится 30 секунд, проблема не в запросе, а в постобработке: обходе большой выборки в цикле, вызовах запросов внутри цикла, построении СКД с ресурсными выражениями.

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

Запрос выполняется долго даже на маленькой базеПроблема в логике запроса

Проверьте соединения, вложенные запросы и функции в условиях

На копии быстро, на рабочей базе медленноПроблема в объёме данных или индексах

Смотрите план запроса и фрагментацию индексов

Медленно только у части пользователейВлияние RLS или параметров сеанса

Проверьте ограничения доступа на уровне записей

Запрос быстрый, но операция медленнаяУзкое место вне запроса

Ищите запросы в цикле и тяжёлую постобработку

Индексы: главный инструмент ускорения

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

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

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

Работа с виртуальными таблицами

Виртуальные таблицы регистров (Остатки, Обороты, СрезПервых, СрезПоследних) — частый источник проблем. Если обратиться к ним без параметров, платформа построит выборку по всей таблице регистра, и только потом вы примените отбор. На больших регистрах это секунды и минуты вместо миллисекунд.

Всегда передавайте отборы в параметры виртуальной таблицы: период, условие по измерениям. Условие «Организация = &Организация» в секции ГДЕ после соединения с виртуальной таблицей «Остатки» не равнозначно тому же условию в параметрах виртуальной таблицы — второй вариант сокращает объём обрабатываемых данных на порядки.

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

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

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

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

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

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

Порядок оптимизации запроса
  1. 1
    Снимите замер: время выполнения запроса на рабочих объёмах данных
  2. 2
    Получите план запроса на СУБД и найдите полные сканирования таблиц
  3. 3
    Перенесите отборы в параметры виртуальных таблиц
  4. 4
    Проверьте индексы по полям отборов и соединений, добавьте недостающие
  5. 5
    Уберите из выборки неиспользуемые поля и лишние соединения
  6. 6
    Разбейте сложный запрос на пакет с временными таблицами
  7. 7
    Повторите замер и сравните план запроса до и после

Типичные ошибки в тексте запроса

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

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

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

Типичные ошибки
01
Отбор в ГДЕ вместо параметров виртуальной таблицы — виртуальная таблица строится по всему регистру
02
Функции над полем в условии соединения или отбора — индекс не используется, полное сканирование
03
Запрос внутри цикла обхода выборки — время растёт пропорционально числу строк
04
Выборка лишних полей и соединений — лишний объём чтения и передачи данных
05
Соединение по полям разных типов — неявное приведение типов отключает индекс
06
Оптимизация по замерам на пустой копии базы — выводы не соответствуют реальной нагрузке

Особенности СКД и динамических списков

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

Для динамических списков критично: отборы должны накладываться на индексированные поля, а произвольный запрос списка — не содержать соединений с виртуальными таблицами без параметров и обращений через точку к реквизитам ссылок («разыменование»). Каждое разыменование в списке — скрытое соединение, которое выполняется при каждом обновлении списка.

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

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

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

План запроса показывает полное сканирование большой таблицыДобавьте индекс по полям отбора или соединения и перепроверьте план
Медленная виртуальная таблица остатков или срезаПеренесите все отборы в параметры виртуальной таблицы
Запрос быстрый, но отчёт строится долгоИщите запросы в цикле, тяжёлые вычисляемые поля СКД и постобработку
Медленно только у пользователей с ограниченными правамиПроверьте шаблоны RLS — сложные ограничения доступа добавляют соединения в каждый запрос
Запрос нельзя ускорить приемлемоВынесите расчёт в фоновое задание с кэшированием результата

Проверка результата и границы самостоятельной работы

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

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

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

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

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

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

Стабильно ниже целевого значения на повторных запусках

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

Ускорение только на первом запуске или на маленьких данных

План запроса
Норма

Index Seek по ключевым таблицам, оценка строк близка к факту

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

Остались Scan по большим таблицам

Результат запроса
Норма

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

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

Расхождения в строках или итогах

Влияние на запись
Норма

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

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

Запись стала ощутимо медленнее — индексов слишком много

Итог

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

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

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

Поможет ли ускорить запрос переход с файловой базы на клиент-серверную?

Сам по себе — не всегда. Клиент-серверный вариант снимает ограничения файлового режима (сетевые блокировки, кэширование), но плохо написанный запрос останется медленным и на MS SQL. Сначала оптимизируйте запрос, а переход рассматривайте как меру при росте числа пользователей и объёма данных.

Почему один и тот же запрос то быстрый, то медленный?

Частые причины: холодный кэш СУБД при первом выполнении, устаревшая статистика, из-за которой оптимизатор выбирает неудачный план для конкретных параметров, и блокировки от параллельных сеансов. Если время сильно зависит от значений параметров — обновите статистику и посмотрите план для «плохого» набора параметров.

Стоит ли добавлять индексы под каждый медленный запрос?

Нет. Каждый индекс замедляет запись и реструктуризацию, занимает место. Добавляйте индекс под конкретный тяжёлый и частый запрос, проверяйте, что план изменился, и следите за скоростью записи. Иногда правильнее переписать запрос, чем покрывать его индексами.

Что быстрее: соединение или вложенный запрос в условии В()?

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

Можно ли ускорить запрос, не меняя конфигурацию?

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

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

Признаки: запрос в одиночку выполняется быстро, но «виснет» при параллельной работе пользователей; в технологическом журнале видны события TLOCK и TTIMEOUT; время ожидания на блокировках растёт в замере. В этом случае оптимизировать нужно транзакции и порядок захвата ресурсов, а не текст запроса.