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