Как ускорить медленные запросы с группировкой в 1С
Содержание
Если отчёт с итогами крутит «часики» минутами, в 9 случаях из 10 виновата не сама группировка, а то, по чему и из чего она считается. Группировка по вычисляемым выражениям, агрегация прямо по таблицам документов вместо регистров и соединение неиндексированных временных таблиц — вот что превращает СГРУППИРОВАТЬ ПО в полное сканирование миллионов строк.
Разберём по шагам, где искать тормоз и чем его лечить: как прочитать план выполнения на стороне СУБД, когда разложить тяжёлый запрос на временные таблицы, почему функция в поле группировки убивает индекс и как заменить агрегацию документов готовыми оборотами регистра. Приёмы работают в клиент-серверном варианте 1С 8.3 на MS SQL Server и PostgreSQL.
С чего начать: замер, а не догадки
Прежде чем переписывать запрос, выясните, что именно тормозит. Оптимизация вслепую чаще ломает логику, чем ускоряет отчёт.
Три инструмента, которые дают факты:
- 📊 Замер производительности в Конфигураторе (
Отладка → Замер производительности) — показывает, какая строка кода и какой запрос съедают время. Начинать всегда отсюда. - 📈 Центр управления производительностью (ЦУП) из состава КИП — собирает долгие запросы с текстом SQL и планом, строит картину по базе за период.
- 🧰 Технологический журнал (
logcfg.xmlс событиемDBMSSQL) — пишет текст SQL, длительность и, при нужной настройке, план выполнения прямо в лог.
Правило простое: сначала найдите самый дорогой запрос по времени, и только потом оптимизируйте его. Запрос, который выполняется раз в сутки за 10 секунд, трогать незачем, даже если он «некрасивый».
В ЦУП и техжурнале смотрите не только длительность, но и число прочитанных строк (Reads). Запрос на 200 строк результата, который прочитал 4 млн строк, — главный кандидат на переделку.
Как прочитать план выполнения запроса
План выполнения — это маршрут, по которому СУБД добывает данные. Именно он показывает, использовался индекс или сервер честно прочитал всю таблицу.
Получить план на MS SQL Server можно так:
- Возьмите текст SQL из техжурнала или ЦУП (событие
DBMSSQL, поле со SQL-текстом). - Откройте его в SQL Server Management Studio.
- Включите
Include Actual Execution Plan(Ctrl+M) и выполните запрос на копии базы.
На что смотреть в плане:
Table ScanилиClustered Index Scanпо большой таблице — красный флаг. Сервер читает таблицу целиком. Нужен индекс или другое условие отбора.Index Seek— хорошо: сервер прыгает сразу к нужным строкам.- Толстые стрелки между операторами — это объём строк. Если перед группировкой через соединение «протекает» миллион строк, проблема в соединении, а не в
СГРУППИРОВАТЬ ПО. - Операторы
Hash MatchиSortс большой стоимостью рядом с группировкой — сервер строит хэш-таблицу или сортирует весь поток в памяти/tempdb.
На PostgreSQL аналог — EXPLAIN ANALYZE: ищите Seq Scan (полное сканирование) вместо Index Scan, а также узлы HashAggregate и GroupAggregate с большим числом строк на входе.
Снимайте план на копии базы, а не на рабочей. Выполнение тяжёлого SQL в SSMS на боевом сервере даёт нагрузку и может повлиять на работу пользователей.
Когда разложить запрос на временные таблицы
Один гигантский запрос с несколькими соединениями и вложенными выборками — типичная причина плохого плана: оптимизатор СУБД не угадывает порядок соединений и ошибается с индексами. Декомпозиция через временные таблицы (ПОМЕСТИТЬ) решает это.
Идея: сначала отберите и агрегируйте данные в маленькую промежуточную таблицу, а потом соединяйте уже её. Чем раньше в запросе отсекаются лишние строки, тем меньше работы на группировке.
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
СУММА(Продажи.Количество) КАК Количество,
СУММА(Продажи.Сумма) КАК Сумма
ПОМЕСТИТЬ ВтПродажи
ИЗ
РегистрНакопления.Продажи.Обороты(&Начало, &Конец, , ) КАК Продажи
СГРУППИРОВАТЬ ПО
Продажи.Номенклатура
ИНДЕКСИРОВАТЬ ПО
Номенклатура
;
ВЫБРАТЬ
Спр.Наименование КАК Наименование,
ВтПродажи.Количество КАК Количество,
ВтПродажи.Сумма КАК Сумма
ИЗ
ВтПродажи КАК ВтПродажи
ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Спр
ПО ВтПродажи.Номенклатура = Спр.Ссылка
Что здесь важно:
- 🔹 Тяжёлая группировка выполняется один раз и складывается в маленькую
ВтПродажи. - 🔹
ИНДЕКСИРОВАТЬ ПО Номенклатура— без этой строки соединение второй таблицы снова пойдёт сканированием. Временные таблицы, участвующие в соединениях, индексируйте по полям связи почти всегда. - 🔹 Соединение со справочником (ради наименований) вынесено из группировки — справочник не участвует в агрегации и не раздувает поток строк.
Декомпозиция особенно выручает, когда в одном запросе смешаны отбор, группировка и соединение с крупными справочниками. Разложите — и каждый этап станет проверяемым по отдельному плану.
Почему функция в поле группировки убивает индекс
Самая частая и незаметная ошибка — группировка или отбор по выражению, а не по полю.
Как только поле в СГРУППИРОВАТЬ ПО или в условии ГДЕ завёрнуто в функцию, индекс по этому полю перестаёт работать: СУБД не может сопоставить вычисленное значение с хранимым индексом и переходит на полное сканирование.
Что именно ломает индекс:
ПРЕДСТАВЛЕНИЕ(Ссылка)в группировке — заставляет вычислять строковое представление для каждой строки.- Приведение типа
ВЫРАЗИТЬ(... КАК ...)в поле связи или группировки. ВЫБОР КОГДА ... ТОГДА ... КОНЕЦкак ключ группировки.- Функции над датой (
НАЧАЛОПЕРИОДА,ДЕНЬ,МЕСЯЦ) в условии отбора по индексированному периоду. - Подстроки и склейка строк в ключе группировки.
Как правильно:
- Группируйте по «сырой» ссылке (
Номенклатура, а не её представление). - Представление и прочие вычисления добавляйте после группировки — во внешнем запросе или соединением со справочником, как в примере выше.
- Для отбора по периоду сравнивайте само поле с границами (
Период >= &Начало И Период < &Конец), а не оборачивайте его вНАЧАЛОПЕРИОДА.
Отдельно про соединения с подзапросами. Соединение с вложенной выборкой, внутри которой есть своя группировка, оптимизатор СУБД часто не может развернуть в эффективный план — он материализует весь подзапрос. Такую конструкцию почти всегда выгоднее разбить на временную таблицу: посчитать подзапрос в ПОМЕСТИТЬ, проиндексировать и соединить уже готовый результат.
Проверка на практике: уберите из СГРУППИРОВАТЬ ПО все функции и представления, оставьте голые ссылки и ключевые поля. Если запрос резко ускорился — вы нашли виновника.
Считайте по регистрам, а не по документам
Частая первопричина медленной группировки — агрегация прямо по таблицам документов. Отчёт перебирает все проведённые реализации за год и суммирует строки табличных частей. Это дорого и почти всегда лишнее.
В 1С для итогов и оборотов есть регистры накопления. Их виртуальные таблицы отдают уже посчитанные данные:
РегистрНакопления.<Имя>.Остатки(&Период, Отбор)— остатки на дату.РегистрНакопления.<Имя>.Обороты(&Начало, &Конец, Периодичность, Отбор)— обороты за период.РегистрНакопления.<Имя>.ОстаткиИОбороты(...)— и то и другое.
Виртуальная таблица оборотов уже агрегирована по измерениям — часть работы, которую вы делали руками через СГРУППИРОВАТЬ ПО, регистр выполнил на этапе проведения и хранит в таблице итогов. Запрос к оборотам по измерению почти всегда быстрее, чем суммирование документов.
Практические приёмы работы с регистрами:
- 📌 Параметры периода и отбора задавайте в скобках виртуальной таблицы, а не в
ГДЕснаружи. Условие в параметрах виртуальной таблицы отсекает строки до агрегации; условие вГДЕ— после, когда сервер уже прочитал всё. - 📌 Передавайте только нужные измерения в отбор — чем уже выборка, тем меньше считать.
- 📌 Параметр периодичности в таблице оборотов (
Период,День,Месяц) берите минимально детальный под задачу. Запрос с периодичностьюАвтоотдаёт итог одной строкой на измерение — это дешевле, чем помесячная детализация, если помесячный разрез отчёту не нужен. - 📌 Выбирайте в запросе только те поля, которые реально выводятся. Лишнее поле в выборке тянет за собой чтение данных и иногда дополнительное соединение;
ВЫБРАТЬ *в сложных запросах — скрытый источник нагрузки. - 📌 Если штатного регистра под отчёт нет, а запрос к документам регулярно тяжёлый, подумайте о собственном регистре накопления или сведений, который наполняется при проведении. Разовая переделка механизма окупается на каждом открытии отчёта.
Сравнение подходов:
| Источник данных | Когда применять | Скорость |
|---|---|---|
| Виртуальная таблица оборотов/остатков | Есть подходящий регистр накопления | Высокая — данные уже агрегированы |
| Основная таблица регистра | Нужны отдельные движения, не итоги | Средняя |
| Таблицы документов с группировкой | Регистра нет, разовая выборка | Низкая на больших периодах |
Индексы и статистика СУБД
Даже правильный запрос тормозит, если под него нет индексов или статистика СУБД устарела и оптимизатор строит план по неверной оценке строк.
Что в вашей зоне ответственности на стороне 1С:
- 🔸 Индексирование измерений регистров. В свойствах измерения есть флаг
Индексировать. Ставьте его для измерений, по которым часто идёт отбор, но не подряд на все — лишние индексы замедляют запись. - 🔸
ИНДЕКСИРОВАТЬ ПОдля временных таблиц по полям соединений и отбора (см. раздел про временные таблицы). - 🔸 Регламентные операции обслуживания. «Тестирование и исправление информационной базы» с реиндексацией пересобирает индексы базы.
Что на стороне администратора СУБД:
- Регулярное обновление статистики. На MS SQL —
UPDATE STATISTICSлибо включённое авто-обновление; на PostgreSQL —ANALYZE(как правило, через autovacuum). - Перестроение и дефрагментация индексов (
REBUILD/REORGANIZEна MS SQL).
Реиндексацию и перестроение индексов планируйте на окно обслуживания, когда пользователи не работают: при «Тестировании и исправлении» база захватывается, а перестроение индексов на рабочей СУБД даёт заметную нагрузку.
Если после обновления статистики план запроса стал другим и быстрее — вы уткнулись именно в устаревшую статистику. Это частая причина «вчера летало, сегодня висит».
Тяжёлые отчёты — в фон и в кэш
Когда запрос оптимизирован, но данных объективно много, остаётся снять ожидание с пользователя: считать заранее и отдавать готовое.
Рабочие варианты:
- 🔁 Фоновое задание. Длинный отчёт запускайте через
ФоновыеЗадания— пользователь не ждёт у замороженной формы, результат приходит по готовности. Для регулярных сводов — по расписанию через регламентное задание ночью. - 💾 Предрасчёт в регистр сведений . Если отчёт строится по одним и тем же срезам, посчитайте их регламентным заданием и сложите в периодический регистр сведений. Открытие отчёта превращается в быстрое чтение готовой таблицы.
- 🧮 Кэш вычислений в коде. Для справочных значений, которые читаются многократно в рамках вызова, помогает
ПовторноеИспользованиеВозвратныхЗначенийу модуля. Это кэш результата функции, а не замена оптимизации самого запроса.
Ещё один эффект предрасчёта — меньше конкуренции за данные. Длинный запрос к оперативным регистрам держит чтение дольше и сильнее пересекается с проведением документов; вынесенный в фон ночной расчёт по готовой таблице этой нагрузки на рабочее время не создаёт.
Выбор между «ускорить запрос» и «посчитать заранее» зависит от того, нужны ли данные в реальном времени. Оперативный остаток считайте запросом к регистру; годовой аналитический свод, который смотрят утром, — предрасчётом в фоне.
Частые вопросы
Группировка в 1С медленнее, чем GROUP BY в чистом SQL?
СГРУППИРОВАТЬ ПО транслируется в обычный GROUP BY СУБД. Разница в скорости — от того, по чему вы группируете и из какой таблицы: агрегация документов против готовых оборотов регистра даёт кратную разницу при том же синтаксисе.Почему запрос быстрый в консоли запросов, но тормозит в отчёте?
Помогает ли `РАЗРЕШЕННЫЕ` или `РАЗЛИЧНЫЕ` ускорить группировку?
РАЗЛИЧНЫЕ может и замедлить — это дополнительная операция устранения дублей. РАЗРЕШЕННЫЕ влияет на применение RLS, а не на скорость группировки. На производительность сильнее действуют источник данных, индексы и функции в ключах.