Разработка и Конфигурация · 12 мин
1С: как ускорить запрос — практическое руководство
Как ускорить запрос в 1С: индексы, виртуальные таблицы, пакетные запросы, типичные ошибки и проверка результата. Практическое руководство для разработчика.
Медленный запрос в 1С — самая частая причина «тормозов» отчётов, проведения документов и обменов данными. Проблема почти никогда в «медленной платформе»: в большинстве случаев запрос написан так, что сервер СУБД вынужден читать миллионы лишних строк, соединять таблицы без индексов или материализовать огромные временные таблицы.
Ускорение запроса — это не одно действие, а последовательность: сначала найти реальное узкое место, затем устранить его минимальным изменением и проверить результат на рабочих объёмах данных. Произвольная «оптимизация на глаз» часто делает хуже: добавленный индекс замедляет запись, а переписанный запрос начинает выбирать больше данных, чем раньше.
Ниже — рабочий порядок действий: как измерить время выполнения, какие приёмы дают эффект чаще всего, где границы безопасной самостоятельной работы и какие ошибки встречаются у разработчиков чаще всего.
Как найти, что именно тормозит
Первое правило оптимизации: не менять запрос, пока не измерено, где теряется время. В управляемом приложении используйте замер производительности (Функциональность — Оценка производительности) или консоль запросов с замером времени выполнения. Для серверной базы полезен технологический журнал с событием DBMSSQL или DBPOSTGRS — он покажет, какой именно SQL-запрос и сколько выполнялся.
Обратите внимание на разницу между временем выполнения запроса на сервере СУБД и общим временем операции. Если запрос отрабатывает за 200 мс, а отчёт строится 30 секунд, проблема не в запросе, а в постобработке: обходе большой выборки в цикле, вызовах запросов внутри цикла, построении СКД с ресурсными выражениями.
Для клиент-серверной базы получите план запроса на стороне СУБД (в MS SQL — через SQL Server Management Studio, в PostgreSQL — через EXPLAIN ANALYZE). План покажет: используется ли индекс, делается ли полное сканирование таблицы (Scan вместо Seek), насколько оценка числа строк расходится с фактом.
Проверьте соединения, вложенные запросы и функции в условиях
Смотрите план запроса и фрагментацию индексов
Проверьте ограничения доступа на уровне записей
Ищите запросы в цикле и тяжёлую постобработку
Индексы: главный инструмент ускорения
Большинство медленных запросов упирается в отсутствие подходящего индекса. Платформа автоматически индексирует стандартные реквизиты (ссылка, дата, номер, период регистра), но реквизиты, по которым вы ставите отбор или соединение, индексируются только если у них включено свойство «Индексировать» или они входят в индекс, заданный в конфигураторе.
Проверьте, что поля в условиях ГДЕ и в условиях соединений покрыты индексами. Для регистров сведений и накопления критичны измерения: отбор по измерению без индекса на большой таблице даёт полное сканирование. Для справочников и документов — реквизиты, по которым регулярно ищут (контрагент, организация, договор).
Индексы не бесплатны: каждый индекс замедляет запись и занимает место. Не включайте индексацию «на всякий случай» для всех реквизитов. Добавляйте индекс под конкретный тяжёлый запрос и проверяйте, что план запроса действительно изменился со Scan на Seek.
Работа с виртуальными таблицами
Виртуальные таблицы регистров (Остатки, Обороты, СрезПервых, СрезПоследних) — частый источник проблем. Если обратиться к ним без параметров, платформа построит выборку по всей таблице регистра, и только потом вы примените отбор. На больших регистрах это секунды и минуты вместо миллисекунд.
Всегда передавайте отборы в параметры виртуальной таблицы: период, условие по измерениям. Условие «Организация = &Организация» в секции ГДЕ после соединения с виртуальной таблицей «Остатки» не равнозначно тому же условию в параметрах виртуальной таблицы — второй вариант сокращает объём обрабатываемых данных на порядки.
Для «СрезПоследних» на больших регистрах сведений рассмотрите альтернативу: если срез нужен по фиксированному набору измерений и часто, иногда выгоднее хранить актуальные значения в отдельном регистре, обновляемом при записи. Это архитектурное решение, а не настройка запроса, но именно оно убирает проблему в корне.
ВЫБРАТЬ
Остатки.Номенклатура,
Остатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, Склад = &Склад) КАК ОстаткиПакетные запросы и временные таблицы
Сложный запрос с несколькими соединениями и группировками часто ускоряется разбиением на пакет: сначала выбираете узкий набор данных во временную таблицу, затем соединяете её с остальными. Временная таблица материализуется один раз, и СУБД работает с сотнями строк вместо миллионов.
Типовой приём: поместить в временную таблицу список документов или номенклатуры, отобранный по жёстким условиям, и дальше соединять виртуальные таблицы регистров уже с этой таблицей, передавая её в параметры отбора. Это резко сокращает объём данных, которые строит виртуальная таблица.
Помните про уничтожение временных таблиц: после использования выполните «УНИЧТОЖИТЬ» или используйте менеджер временных таблиц с явным закрытием. В долгоживущих серверных вызовах накопленные временные таблицы расходуют память и tempdb.
- 1Снимите замер: время выполнения запроса на рабочих объёмах данных
- 2Получите план запроса на СУБД и найдите полные сканирования таблиц
- 3Перенесите отборы в параметры виртуальных таблиц
- 4Проверьте индексы по полям отборов и соединений, добавьте недостающие
- 5Уберите из выборки неиспользуемые поля и лишние соединения
- 6Разбейте сложный запрос на пакет с временными таблицами
- 7Повторите замер и сравните план запроса до и после
Типичные ошибки в тексте запроса
Некоторые конструкции систематически приводят к медленной работе независимо от индексов. Соединение по условию с функцией или выражением (например, по ПОДСТРОКА(Поле, 1, 10)) отключает использование индекса. Соединение таблиц по полям разного типа — тоже: неявное приведение типов заставляет СУБД сканировать таблицу целиком.
Опасна выборка «ВЫБРАТЬ * » через обращение к таблице целиком и лишние поля в выборке: каждое поле — это данные, которые нужно прочитать и передать. Особенно дорого обходятся поля неограниченной длины и соединения «на всякий случай», результаты которых в коде не используются.
Отдельная тема — запросы в цикле. Если внутри обхода выборки выполняется ещё один запрос, время растёт линейно от числа строк. Практически всегда такой код переписывается одним запросом с соединением или пакетом: сначала выбрать все нужные данные одним запросом, потом обходить результат.
Особенности СКД и динамических списков
В отчётах на СКД итоговый запрос собирается из нескольких наборов данных, и медленным может быть не основной запрос, а вычисляемые поля, ресурсы с выражениями или связи наборов данных. Проверяйте запрос, который реально уходит на СУБД, — его видно в технологическом журнале или через расшифровку макета компоновки.
Для динамических списков критично: отборы должны накладываться на индексированные поля, а произвольный запрос списка — не содержать соединений с виртуальными таблицами без параметров и обращений через точку к реквизитам ссылок («разыменование»). Каждое разыменование в списке — скрытое соединение, которое выполняется при каждом обновлении списка.
Если отчёт используется регулярно с одними и теми же параметрами, рассмотрите кэширование: сохранение результата в регистр сведений и обновление по расписанию. Пользователь получает мгновенный отчёт, а тяжёлый запрос выполняется фоновым заданием в нерабочее время.
Как выбрать действие
Проверка результата и границы самостоятельной работы
Оптимизация считается завершённой, когда есть два замера — до и после — на одинаковых данных, и план запроса подтверждает, что узкое место устранено. Разовый замер обманчив: первый запуск может быть медленным из-за холодного кэша СУБД. Выполняйте замер минимум дважды и сравнивайте повторные запуски.
Проверяйте не только скорость, но и корректность: переписанный запрос должен вернуть тот же набор данных. Сравните результаты старого и нового вариантов на нескольких наборах параметров, включая граничные случаи — пустой период, отсутствие данных, большой отбор.
Самостоятельно безопасно: менять текст запроса, включать индексацию реквизитов, переносить отборы в параметры виртуальных таблиц. К участию администратора СУБД или привлечению специалиста стоит переходить, когда нужно перестраивать индексы на больших таблицах, настраивать технологический журнал, менять параметры СУБД или разбираться с блокировками и взаимоблокировками.
Как проверить результат
Стабильно ниже целевого значения на повторных запусках
Ускорение только на первом запуске или на маленьких данных
Index Seek по ключевым таблицам, оценка строк близка к факту
Остались Scan по большим таблицам
Совпадает с результатом старого варианта на всех тестовых наборах
Расхождения в строках или итогах
Проведение документов не замедлилось заметно после добавления индекса
Запись стала ощутимо медленнее — индексов слишком много
Что важно запомнить
Ускорение запроса начинается с замера и плана выполнения, а не с переписывания текста. Основные приёмы: индексы по полям отборов и соединений, отборы в параметрах виртуальных таблиц, устранение запросов в цикле, пакетные запросы с временными таблицами. Результат подтверждайте повторным замером на реальных данных и сравнением выборок до и после.
Частые вопросы
Поможет ли ускорить запрос переход с файловой базы на клиент-серверную?
Сам по себе — не всегда. Клиент-серверный вариант снимает ограничения файлового режима (сетевые блокировки, кэширование), но плохо написанный запрос останется медленным и на MS SQL. Сначала оптимизируйте запрос, а переход рассматривайте как меру при росте числа пользователей и объёма данных.
Почему один и тот же запрос то быстрый, то медленный?
Частые причины: холодный кэш СУБД при первом выполнении, устаревшая статистика, из-за которой оптимизатор выбирает неудачный план для конкретных параметров, и блокировки от параллельных сеансов. Если время сильно зависит от значений параметров — обновите статистику и посмотрите план для «плохого» набора параметров.
Стоит ли добавлять индексы под каждый медленный запрос?
Нет. Каждый индекс замедляет запись и реструктуризацию, занимает место. Добавляйте индекс под конкретный тяжёлый и частый запрос, проверяйте, что план изменился, и следите за скоростью записи. Иногда правильнее переписать запрос, чем покрывать его индексами.
Что быстрее: соединение или вложенный запрос в условии В()?
Однозначного ответа нет — зависит от данных и СУБД. На практике соединение с небольшой временной таблицей обычно предсказуемее, а длинные списки значений в условии В() могут ухудшать план. Проверяйте оба варианта замером на своих данных.
Можно ли ускорить запрос, не меняя конфигурацию?
Частично: обновление статистики и перестроение индексов средствами СУБД, настройка параметров сервера, уменьшение объёма выбираемых данных в настройках отчёта. Но устойчивый эффект обычно требует изменения текста запроса или индексирования реквизитов, то есть доработки конфигурации.
Как понять, что проблема в блокировках, а не в самом запросе?
Признаки: запрос в одиночку выполняется быстро, но «виснет» при параллельной работе пользователей; в технологическом журнале видны события TLOCK и TTIMEOUT; время ожидания на блокировках растёт в замере. В этом случае оптимизировать нужно транзакции и порядок захвата ресурсов, а не текст запроса.