Медленные запросы в 1С: найти, разобрать план и переписать

· 10 мин

Содержание

Оптимизация запросов в 1С начинается не с переписывания кода, а с замера: найдите 5–10 запросов, которые дольше всех держат базу, и правьте их по порядку. Чаще всего ускорение дают четыре правки: условия переносят в параметры виртуальных таблиц, убирают разыменование составных полей, выносят запросы из циклов и индексируют временные таблицы.

Прирост в 40% и больше реален, но не гарантирован. Если тормозит один отчёт из-за плохого запроса, после правки он может работать в десятки раз быстрее. Если база упирается в диск или память сервера, правка запросов даст меньше, и нужно смотреть на обслуживание СУБД.

Где искать медленные запросы: диагностика до правок

Не угадывайте. Запрос, который кажется тяжёлым, может выполняться за 50 мс, а настоящий виновник прячется в проведении документа или в регламентном задании.

Замер производительности в Конфигураторе

Подходит, когда известна конкретная операция: «долго проводится реализация», «медленно формируется отчёт».

  1. Запустите Конфигуратор и режим отладки: Отладка → Начать отладку → 1С:Предприятие.
  2. В Конфигураторе включите Отладка → Замер производительности.
  3. В пользовательском режиме выполните медленную операцию.
  4. Выключите замер. Откроется таблица строк кода с числом вызовов и долей времени.
  5. Отсортируйте по времени. Строка с методом Выполнить() и большой долей — ваш запрос.

💡 Смотрите не только на время одной строки, но и на колонку количества вызовов. Запрос на 20 мс, выполненный 3 000 раз, хуже запроса на 5 секунд, выполненного один раз.

Технологический журнал

Подходит для рабочей базы, где тормозит «всё» и непонятно что. Журнал пишет каждый запрос к СУБД дольше заданного порога, а для MS SQL ещё и план выполнения.

Настройка лежит в файле logcfg.xml в каталоге conf платформы (например, C:\Program Files\1cv8\conf\) на сервере 1С. Пример — запросы дольше 1 секунды:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
  <log location="D:\1c_techlog" history="24">
    <event>
      <eq property="name" value="DBMSSQL"/>
      <gt property="duration" value="1000000"/>
    </event>
    <event>
      <eq property="name" value="DBPOSTGRS"/>
      <gt property="duration" value="1000000"/>
    </event>
    <property name="all"/>
  </log>
  <plansql/>
</config>
  • ⏱ duration в 8.3 указывается в микросекундах: 1000000 — это 1 секунда.
  • 📁 history="24" — журнал хранится 24 часа, старые файлы удаляются сами.
  • 📝 <plansql/> добавляет в журнал планы запросов MS SQL.
  • 🧩 Свойство Context в записи показывает строку модуля, откуда пришёл запрос.

Журнал без фильтра по длительности за час забивает десятки гигабайт и сам тормозит сервер. Ставьте порог, держите каталог на отдельном диске и выключайте журнал после сбора данных, удалив или переименовав logcfg.xml.

Что смотреть в собранных данных

Группируйте записи по тексту запроса и считайте суммарное время. Первые 5–10 строк такого рейтинга обычно дают больше половины нагрузки. Именно их и оптимизируйте, остальное можно не трогать.

Как переписать текст запроса: условия, соединения, вложенные запросы

Платформа переводит запрос на языке 1С в SQL для конкретной СУБД. Неудачная конструкция в 1С превращается в тяжёлый SQL, который никакой сервер не вытянет.

Запрос в цикле

Самая частая причина медленного проведения и обработок. Вместо тысячи запросов по одному товару — один запрос по всему списку.

Было:

Для Каждого Строка Из Товары Цикл
    Запрос = Новый Запрос("ВЫБРАТЬ Цена ИЗ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(, Номенклатура = &Номенклатура)");
    Запрос.УстановитьПараметр("Номенклатура", Строка.Номенклатура);
    Выборка = Запрос.Выполнить().Выбрать();
    // ...
КонецЦикла;

Стало:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|    Цены.Номенклатура КАК Номенклатура,
|    Цены.Цена КАК Цена
|ИЗ
|    РегистрСведений.ЦеныНоменклатуры.СрезПоследних(, Номенклатура В (&СписокНоменклатуры)) КАК Цены";
Запрос.УстановитьПараметр("СписокНоменклатуры", Товары.ВыгрузитьКолонку("Номенклатура"));
ТаблицаЦен = Запрос.Выполнить().Выгрузить();
ТаблицаЦен.Индексы.Добавить("Номенклатура");

Дальше в цикле ищите цену через ТаблицаЦен.Найти(Строка.Номенклатура, "Номенклатура") — это поиск в памяти по индексу, без обращения к серверу СУБД.

Разыменование составных полей

Поле Регистратор у регистра или реквизит составного типа может ссылаться на десятки видов документов. Обращение Регистратор.Дата заставляет платформу соединить запрос со всеми этими таблицами.

// Плохо: соединение со всеми документами-регистраторами
ВЫБРАТЬ Движения.Регистратор.Дата ИЗ РегистрНакопления.ТоварыНаСкладах КАК Движения

// Лучше: только одна таблица
ВЫБРАТЬ ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Дата
ИЗ РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг

Для обычной даты движения вообще не нужен регистратор: у регистра есть поле Период.

ИЛИ в условиях и соединениях

Условие вида ГДЕ Склад = &Склад1 ИЛИ Организация = &Организация часто мешает СУБД использовать индекс, и она читает таблицу целиком. Разбейте его на два запроса через ОБЪЕДИНИТЬ ВСЕ, каждый со своим простым условием. Если строки могут совпасть, используйте ОБЪЕДИНИТЬ без ВСЕ, но он дороже из-за удаления дублей.

Вложенные запросы в соединениях

Соединение с подзапросом (ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ ...) КАК Т) оптимизатор СУБД оценивает плохо: он не знает, сколько строк вернёт подзапрос. Выносите подзапрос во временную таблицу, а соединяйте уже с ней.

Индексы: что индексирует платформа и что можно добавить

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

ОбъектИндексКак влиять
Реквизит справочника или документаНет по умолчаниюСвойство Индексировать или Индексировать с доп. упорядочиванием
Измерения регистраСоздаётся платформойПорядок измерений: первым — то, по которому чаще отбор
Временная таблицаНет по умолчаниюИНДЕКСИРОВАТЬ ПО в тексте запроса
Ссылка, код, наименованиеСоздаются платформойНе требуется

Индекс на временной таблице

Если временная таблица потом участвует в соединении или отборе и в ней тысячи строк, индекс резко ускоряет следующий шаг:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура,
    Товары.Склад КАК Склад
ПОМЕСТИТЬ ВТ_Товары
ИЗ
    &ТаблицаТоваров КАК Товары
ИНДЕКСИРОВАТЬ ПО
    Номенклатура,
    Склад

Для таблицы на 10–20 строк индекс бесполезен: его построение дороже выигрыша.

Порядок измерений регистра

Индекс по измерениям составной, СУБД эффективно использует его только с первого поля. Если в регистре первое измерение Организация, а отчёт отбирает только по Номенклатуре, индекс работает плохо. Менять порядок измерений в типовой конфигурации нельзя без снятия с поддержки, а в своей разработке продумайте его заранее.

Виртуальные таблицы регистров: параметры вместо ГДЕ

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

Главное правило

Условие по измерениям ставьте в параметры виртуальной таблицы, а не в ГДЕ:

// Медленно: сначала считаются остатки по всем складам, потом отбор
ВЫБРАТЬ Остатки.Номенклатура, Остатки.КоличествоОстаток
ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки
ГДЕ Остатки.Склад = &Склад

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

Условие по ресурсам (КоличествоОстаток > 0) в параметры поставить нельзя — оно остаётся в ГДЕ.

Ещё несколько приёмов

  • 📅 Остатки на текущий момент без параметра даты читаются из таблицы итогов почти мгновенно. Не подставляйте ТекущаяДата(), если нужен актуальный остаток: передайте пустой период.
  • 🔗 Не соединяйте виртуальную таблицу с другой таблицей, чтобы отфильтровать её. Передайте отбор в параметр: Номенклатура В (ВЫБРАТЬ Номенклатура ИЗ ВТ_Товары).
  • 🔢 ОстаткиИОбороты с периодичностью День за год вернёт по 365 строк на каждую позицию. Если нужен итог за период, не указывайте периодичность или ставьте Период.
  • 🧮 Медленные остатки на прошлые даты часто лечатся пересчётом итогов: Все функции → Стандартные → Управление итогами. Пересчёт нагружает базу, запускайте его вне рабочего времени.

Как читать план выполнения запроса

План показывает, как СУБД на самом деле достаёт данные. Для MS SQL его даёт технологический журнал с <plansql/>, для PostgreSQL — EXPLAIN ANALYZE или расширение auto_explain на стороне сервера СУБД.

Текст SQL от платформы читать непросто: таблицы называются _Document123, _AccumRg456. Соответствие имён объектам конфигурации возвращает метод ПолучитьСтруктуруХраненияБазыДанных().

На что смотреть в плане

Признак в планеЧто значитЧто делать
Table Scan / Clustered Index Scan (MS SQL), Seq Scan (PostgreSQL) по большой таблицеЧтение таблицы целикомПроверить условие: попадает ли оно в индекс, нет ли функции над полем
Nested Loops при больших объёмах обеих сторонСУБД ошиблась в оценке числа строкОбновить статистику, вынести часть во временную таблицу
Оценка строк 1, факт — 100 000Устаревшая статистикаОбновление статистики СУБД
Key Lookup много разИндекс есть, но не покрывает поляСократить список полей или изменить индексацию реквизита
Sort или Hash с выгрузкой в tempdb / на дискНе хватает памяти на операциюСократить объём выборки, проверить настройки памяти СУБД

💡 Сравнивайте оценочное и фактическое число строк. Расхождение в сотни раз почти всегда указывает на статистику или на подзапрос, который оптимизатор не смог оценить.

Настройка MS SQL Server и PostgreSQL под 1С

Один и тот же запрос на разных СУБД ведёт себя по-разному. Временные таблицы, соединения с подзапросами и ИЛИ PostgreSQL исторически переваривает хуже, поэтому приёмы из прошлых разделов там дают особенно заметный эффект.

MS SQL Server

  • 📊 Обновление статистики — ежедневно, ночью. Без неё оптимизатор строит планы на старых данных.
  • 🧱 Дефрагментация индексов — еженедельно: реорганизация при умеренной фрагментации, перестроение при сильной.
  • ⚙️ Max degree of parallelism = 1 — рекомендация для баз 1С, параллельные планы часто дают блокировки и всплески нагрузки.
  • 🧹 Очистка процедурного кэша после обновления статистики, чтобы сервер не держал старые планы.

Всё это собирается в план обслуживания (Maintenance Plan) в SQL Server Management Studio.

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

PostgreSQL

  • 🐘 Используйте сборку PostgreSQL с патчами для 1С (от фирмы 1С или Postgres Pro). Обычный PostgreSQL без них работает с 1С заметно медленнее.
  • 🧽 Следите за autovacuum: без него таблицы раздуваются, а статистика устаревает. Для больших таблиц регистров регулярно запускайте VACUUM ANALYZE.
  • 🧠 Ключевые параметры памяти: shared_buffers (ориентир — около 25% ОЗУ), effective_cache_size, work_mem. Слишком большой work_mem при множестве сеансов съест всю память.
  • 📈 Расширение online_analyze из сборки для 1С собирает статистику по временным таблицам сразу после их заполнения. Проверьте, что оно включено.

Пример ручного обслуживания одной таблицы:

VACUUM (ANALYZE, VERBOSE) _accumrg1234;

Инструменты и автоматизация контроля

Разовая оптимизация живёт до следующего обновления конфигурации или роста базы. Контроль лучше поставить на поток.

ИнструментЧто даётКогда брать
Замер производительностиВремя по строкам кодаРазбор одной операции
Технологический журналВсе медленные запросы, планы, контекстРабочая база, поиск виновника
Консоль запросов (обработка)Отладка текста, время выполненияПереписывание и проверка запроса
Центр управления производительностью (ЦУП) из КИПМониторинг, рейтинги запросов, APDEXКрупные базы, постоянный контроль
Планы обслуживания MS SQL / cron + vacuumdbСтатистика и индексы по расписаниюЛюбая клиент-серверная база

Простой вариант без покупки инструментов: скрипт, который раз в сутки разбирает файлы технологического журнала, группирует записи DBMSSQL или DBPOSTGRS по Context и присылает топ-10 по суммарному времени. Рост какой-то строки в рейтинге — сигнал смотреть её до того, как пожалуются пользователи.

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

Проверка запроса перед выпуском
Выполнено: 0 / 7

Ошибки, которые сводят оптимизацию на нет

  • ❌ Проверка на пустой базе. В демо-базе на 100 документов любой запрос быстрый. Тестируйте на копии рабочей базы с реальным объёмом.
  • ❌ Оптимизация «на глаз». Переписали красивый, но редкий запрос, а тормозил другой. Без журнала или замера время уходит впустую.
  • ❌ ПЕРВЫЕ 1 для проверки существования без индекса. Если поле отбора не проиндексировано, СУБД всё равно может просмотреть всю таблицу.
  • ❌ Функции над полем в условии. ГОД(Документ.Дата) = 2025 не использует индекс по дате. Пишите диапазон: Документ.Дата МЕЖДУ &Начало И &Конец.
  • ❌ ВЫБРАТЬ РАЗЛИЧНЫЕ как лекарство от дублей. Дубли обычно означают неверное соединение. РАЗЛИЧНЫЕ прячет ошибку и добавляет сортировку.
  • ❌ Правка типовой конфигурации напрямую. После обновления изменения придётся переносить вручную. Для типовых решений используйте расширения конфигурации .

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

Почему запрос быстрый в консоли и медленный в программе?
В консоли вы часто подставляете другие параметры или выполняете его повторно, когда данные уже в кэше СУБД. Ещё одна причина — ограничения доступа на уровне записей (RLS): под пользователем с ограничениями платформа добавляет к запросу дополнительные условия.
Помогает ли перенос базы на SSD?
Да, если запросы упираются в чтение с диска: это видно по большим ожиданиям ввода-вывода на сервере СУБД. Но плохой план выполнения SSD не исправит, он только сделает полный просмотр таблицы чуть быстрее.
Нужно ли ставить «Индексировать» на все реквизиты?
Нет. Каждый индекс замедляет запись документа и занимает место. Индексируйте только реквизиты, по которым реально часто отбирают или соединяют.
Что лучше для 1С: MS SQL Server или PostgreSQL?
Обе СУБД официально поддерживаются. MS SQL прощает больше неудачных запросов, PostgreSQL требует аккуратнее писать запросы и обслуживать базу, но не требует лицензий Microsoft.
Как понять, что тормозят блокировки, а не запрос?
Если в технологическом журнале долгие события ожидания блокировок (TLOCK, TTIMEOUT), а сам запрос в консоли выполняется быстро, проблема в конкуренции сеансов за данные, а не в тексте запроса.