Медленный запрос в 1С 8.3: найти узкое место и переписать
Содержание
Чтобы ускорить запрос в 1С, сначала найдите медленный запрос замером или технологическим журналом, затем посмотрите его план выполнения в СУБД и только после этого переписывайте текст. Чаще всего помогают три правки: отбор переносят в параметры виртуальной таблицы, соединение с подзапросом заменяют временной таблицей с ИНДЕКСИРОВАТЬ ПО, а условие с ИЛИ разбивают на ОБЪЕДИНИТЬ ВСЕ.
Оптимизация вслепую почти всегда тратит время. Запрос, который «на глаз» выглядит тяжёлым, может выполняться за 50 мс, а тормозит соседняя строка кода, которая вызывает запрос в цикле 3000 раз. Поэтому порядок такой: замер → план → правка → повторный замер.
Как найти медленный запрос: замер, консоль и технологический журнал
Замер производительности в конфигураторе
Самый быстрый способ понять, где теряется время, если есть доступ к конфигуратору:
- Запустите базу в режиме отладки из конфигуратора.
- Включите
Отладка → Замер производительности. - Выполните в пользовательском режиме медленную операцию: проведение, формирование отчёта, открытие формы.
- Снова нажмите
Отладка → Замер производительности, чтобы остановить замер.
Конфигуратор покажет строки кода, количество вызовов и долю времени. Ищите две картины:
- 🔴 одна строка
Запрос.Выполнить()занимает 70–90% времени — проблема в самом запросе; - 🟠 строка с запросом занимает немного на один вызов, но вызывается тысячи раз — это запрос в цикле, текст оптимизировать бесполезно, нужно менять алгоритм.
Проверка в консоли запросов
Скопируйте текст запроса в обработку «Консоль запросов» и выполните его с теми же параметрами, что передаёт код: та же дата, тот же склад, тот же пользователь. Консоль показывает время выполнения, и на ней удобно сравнивать варианты переписанного текста.
Замеряйте дважды. Первый запуск читает данные с диска, второй берёт их из кэша СУБД. Сравнивайте варианты запроса по второму и последующим запускам, иначе выиграет тот, который запускали позже.
Технологический журнал для рабочей базы
Когда тормозит у пользователей, а у вас всё быстро, нужен технологический журнал (ТЖ). Он пишет все обращения к СУБД дольше заданного порога. Настройки лежат в файле logcfg.xml в каталоге conf платформы, например C:\Program Files\1cv8\conf\.
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
<log location="D:\v8log" history="24">
<event>
<eq property="name" value="DBMSSQL"/>
<ge property="duration" value="1000000"/>
</event>
<event>
<eq property="name" value="DBPOSTGRS"/>
<ge property="duration" value="1000000"/>
</event>
<property name="all"/>
</log>
<plansql/>
</config>
Здесь порог — 1 секунда: в 8.3 длительность в журнале указывается в микросекундах. Параметр history="24" хранит журнал 24 часа. Элемент <plansql/> добавляет к событиям план выполнения запроса.
ТЖ без фильтра по длительности за час забивает диск на десятки гигабайт. Пишите журнал на отдельный диск, включайте на ограниченное время и удаляйте logcfg.xml сразу после сбора данных.
В событии DBMSSQL или DBPOSTGRS есть свойства Sql (текст запроса на языке СУБД), Context (модуль и строка кода 1С), Rows (сколько строк вернулось) и длительность. По Context вы сразу находите место в конфигурации.
Как читать план выполнения запроса
План — это то, как СУБД на самом деле достаёт данные. Запрос 1С в нём выглядит иначе: вместо Документ.РеализацияТоваровУслуг будут таблицы вида _Document123, вместо реквизитов — _Fld4567.
Как сопоставить имена таблиц
Соответствие имён возвращает метод ПолучитьСтруктуруХраненияБазыДанных(). Выполните его во внешней обработке или в режиме отладки:
Структура = ПолучитьСтруктуруХраненияБазыДанных(, Истина);
Для Каждого Строка Из Структура Цикл
Если СтрНайти(Строка.Метаданные, "РеализацияТоваровУслуг") > 0 Тогда
Сообщить(Строка.Метаданные + " = " + Строка.ИмяТаблицыХранения);
КонецЕсли;
КонецЦикла;
Второй параметр Истина возвращает имена в терминах СУБД, а не в терминах 1С.
Где смотреть план
- 🗄 MS SQL Server: в SQL Server Management Studio вставьте текст из свойства
Sqlи включите фактический план выполнения (Include Actual Execution Plan,Ctrl+M). С<plansql/>план уже есть в ТЖ, в свойствеplanSQLText. - 🐘 PostgreSQL: выполните запрос с префиксом
EXPLAIN (ANALYZE, BUFFERS)вpsqlили pgAdmin.
На что смотреть в плане
| Что видно | Что это значит | Что делать |
|---|---|---|
Index Seek (MS SQL), Index Scan по условию (PostgreSQL) | Поиск по индексу, нормально | Ничего |
Clustered Index Scan, Table Scan, Seq Scan по большой таблице | Читается вся таблица | Искать условие, которое не попадает в индекс |
Nested Loops с миллионами строк во внутреннем цикле | Соединение выбрано по неверной оценке | Временная таблица, свежая статистика |
| Оценка строк 1, фактически 200 000 | Устаревшая статистика или подзапрос | Обновить статистику, вынести подзапрос |
Key Lookup с большим числом выполнений | Индекс есть, но за полями ходят в основную таблицу | Сократить список полей или добавить индексируемый реквизит |
Главный сигнал — расхождение оценочного и фактического числа строк. Если СУБД ждала 10 строк, а получила 100 000, она выбрала план для 10 строк, и он работает в разы медленнее.
Как ускорить запрос: условия отбора и соединения
Соединение с подзапросом → временная таблица
СУБД плохо оценивает число строк подзапроса и часто выбирает Nested Loops там, где нужен другой тип соединения. Вынесите подзапрос во временную таблицу и проиндексируйте её по полю соединения:
ВЫБРАТЬ
Заказы.Ссылка КАК Заказ,
Заказы.Контрагент КАК Контрагент
ПОМЕСТИТЬ ВТ_Заказы
ИЗ
Документ.ЗаказКлиента КАК Заказы
ГДЕ
Заказы.Дата МЕЖДУ &НачалоПериода И &КонецПериода
И Заказы.Проведен
ИНДЕКСИРОВАТЬ ПО
Контрагент
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Заказы.Заказ,
Контрагенты.ИНН
ИЗ
ВТ_Заказы КАК ВТ_Заказы
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
ПО ВТ_Заказы.Контрагент = Контрагенты.Ссылка
Имя документа здесь для примера, подставьте объект своей конфигурации. Индекс временной таблицы окупается от нескольких тысяч строк. Для таблицы на 50 строк ИНДЕКСИРОВАТЬ ПО только добавит накладных расходов.
Условие с ИЛИ → ОБЪЕДИНИТЬ ВСЕ
Условие Поле1 = &А ИЛИ Поле2 = &Б часто приводит к полному чтению таблицы, хотя по каждому полю есть индекс. Два запроса, объединённые через ОБЪЕДИНИТЬ ВСЕ, используют каждый свой индекс. Если строка может попасть в обе части, используйте ОБЪЕДИНИТЬ без ВСЕ: он уберёт дубли, но работает медленнее.
Составные поля и разыменование через точку
Регистратор.Дата или Документ.Контрагент.ИНН по полю составного типа — классическая причина тормозов. Если поле может ссылаться на 30 видов документов, платформа соединит таблицу со всеми тридцатью. Уточните тип через ВЫРАЗИТЬ:
ВЫБРАТЬ
ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент
ИЗ
РегистрНакопления.Продажи КАК Движения
ГДЕ
Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
Функции над полем в условии
Условие ГОД(Заказы.Дата) = 2025 не использует индекс по дате: СУБД вычисляет функцию для каждой строки. Тот же отбор через Заказы.Дата МЕЖДУ &Начало И &Конец идёт по индексу. То же с ПОДОБНО "%текст%": шаблон с % в начале индекс не использует никогда.
Запрос в цикле
Если замер показал тысячи вызовов одного запроса, соберите ссылки в массив и сделайте один запрос с условием В (&СписокСсылок). Один запрос на 3000 строк выполняется быстрее, чем 3000 запросов по одной строке, даже если каждый из них занимает 5 мс.
Индексы и полнотекстовый поиск в 1С
Какие индексы создаёт платформа
Индексы в 1С задают в конфигураторе, а не в СУБД. У реквизитов справочников и документов есть свойство «Индексировать» с тремя вариантами:
- «Не индексировать»;
- «Индексировать» — индекс по полю;
- «Индексировать с доп. упорядочиванием» — индекс дополнительно включает поля упорядочивания, полезно для динамических списков с сортировкой.
Ссылка, дата и номер документа, код и наименование справочника индексируются платформой сами.
Не создавайте индексы прямо в MS SQL или PostgreSQL. При следующей реструктуризации базы платформа их удалит, а иногда они ломают обновление конфигурации. Индекс добавляйте свойством в конфигураторе или в расширении.
Порядок измерений регистра
У регистров индекс строится по измерениям в том порядке, в котором они идут в дереве метаданных. Если первое измерение — «Организация», а запрос отбирает только по «Номенклатуре», основной индекс работает плохо. Для измерения, по которому часто отбирают без первых измерений, включите свойство «Индексировать».
Каждый лишний индекс замедляет запись. На регистре, куда пишут сотни документов в минуту, индекс «на всякий случай» вредит.
Полнотекстовый поиск вместо ПОДОБНО
Поиск по фрагменту строки через ПОДОБНО "%...%" на справочнике в 500 000 элементов читает его целиком. Полнотекстовый индекс решает эту задачу, но работает не через язык запросов, а через объект ПолнотекстовыйПоиск:
Список = ПолнотекстовыйПоиск.СоздатьСписок("подшипник 6205", 50);
Список.ПерваяЧасть();
Для Каждого Элемент Из Список Цикл
Сообщить(Элемент.Значение);
КонецЦикла;
Индекс включают и обновляют в разделе Администрирование; в типовых конфигурациях на БСП для этого есть настройка полнотекстового поиска и регламентное задание обновления индекса. Если индекс не обновляется, новые элементы в результатах поиска не появятся.
Виртуальные таблицы регистров: отбор в параметрах
Виртуальные таблицы Остатки, Обороты, ОстаткиИОбороты, СрезПоследних строятся по итогам и движениям регистра. Отбор в ГДЕ применяется уже после того, как таблица собрана целиком по всем складам и всей номенклатуре.
Медленно:
ВЫБРАТЬ
Остатки.Номенклатура,
Остатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки
ГДЕ
Остатки.Склад = &Склад
Быстро:
ВЫБРАТЬ
Остатки.Номенклатура,
Остатки.КоличествоОстаток
ИЗ
РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата, Склад = &Склад) КАК Остатки
Правила для параметров виртуальных таблиц:
- ✅ в условие параметра пишите только поля самой виртуальной таблицы (измерения);
- ✅ отбор по списку —
Номенклатура В (&Список)илиВ (ВЫБРАТЬ ... ИЗ ВТ_Товары)по временной таблице; - ❌ не соединяйте виртуальную таблицу с подзапросом — сначала временная таблица;
- ❌ не берите
ОстаткиИОбороты, если нужны только остатки: эта таблица тяжелее.
Итоги регистров
Остатки на текущую дату берутся из итогов быстро. На прошлую дату платформа берёт итоги ближайшего периода и досчитывает движения. Если итоги рассчитаны год назад, каждый запрос остатков перебирает движения за год.
Проверьте это обработкой «Управление итогами» (Все функции → Стандартные → Управление итогами). Установите период рассчитанных итогов на начало текущего или прошлого месяца. В типовых конфигурациях это делает регламентное задание, но оно бывает отключено.
Пересчёт итогов на большой базе идёт долго и нагружает сервер. Запускайте его в нерабочее время: пользователи в это время будут видеть замедление, а проведение документов может ждать.
Настройки сервера 1С и СУБД для быстрых запросов
Если запросы тормозят все разом, а не один конкретный, проблема чаще в обслуживании СУБД, а не в тексте.
MS SQL Server
| Что | Как часто | Зачем |
|---|---|---|
| Обновление статистики | Ежедневно ночью | Без статистики СУБД неверно оценивает число строк |
| Очистка процедурного кэша после статистики | Сразу после обновления | Чтобы старые планы не жили дальше |
| Перестроение или реорганизация индексов | Еженедельно | Фрагментированный индекс читается медленнее |
| Max degree of parallelism = 1 | Один раз | Рекомендация для баз 1С: параллельные планы дают нестабильное время |
| Max server memory | Один раз | Оставить память ОС и серверу 1С, если они на той же машине |
Задания делают через планы обслуживания (Maintenance Plans) в SQL Server Management Studio.
PostgreSQL
- 🐘 используйте сборку PostgreSQL с патчами для 1С, а не «ванильную»;
- 🧹 autovacuum должен быть включён; после массовой загрузки данных выполните
VACUUM ANALYZE; - ⚙️
shared_buffers— порядка 25% памяти сервера,effective_cache_size— 50–75%; - ⚙️
work_memподнимайте осторожно: он выделяется на каждую операцию сортировки в каждом соединении.
Сервер 1С
- проверьте, что база клиент-серверная: файловая база на 10+ пользователях с тяжёлыми отчётами упирается в сам формат;
- режим управления блокировкой данных — «Управляемый»: при автоматическом режиме запросы ждут табличных блокировок;
- серверу 1С и СУБД на одной машине нужна память обоим, нехватка RAM приводит к чтению с диска.
Когда запрос медленный не из-за своего текста
Бывает, что запрос написан правильно, а пользователь всё равно ждёт. Разберите по симптому:
Очистка процедурного кэша и обновление статистики на время замедляют все запросы базы, пока СУБД строит планы заново. Делайте это вне рабочих часов.
Частые вопросы
Можно ли ускорить типовой отчёт, не снимая конфигурацию с поддержки?
Помогает ли ВЫБРАТЬ ПЕРВЫЕ ускорить запрос?
Сколько временных таблиц можно использовать в одном пакете?
Почему после переноса базы с MS SQL на PostgreSQL запросы стали медленнее?
VACUUM ANALYZE, проверьте настройки памяти и найдите через ТЖ запросы, которые стали медленнее, — их план придётся разбирать отдельно.