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