Бухгалтер открывает «Отчёт по продажам за месяц». Нажимает «Сформировать». Платформа задумывается. Через полминуты — индикатор. Через две минуты — индикатор. Через пять минут — отчёт. Бухгалтер уже сходила за кофе, проверила почту, ответила на два звонка. Дальше она идёт жаловаться, что «1С тормозит».
Открываете отчёт под собой — секунд за двадцать. Под бухгалтером — пять минут. Один и тот же отчёт, одна и та же база. Разница — в данных, в правах и в настройках варианта. И это вторая по частоте история про СКД, после «отчёт показывает не то».
Где время уходит у медленного отчёта
Прежде чем оптимизировать, нужно понять — где время. У отчёта на СКД оно может уходить в трёх местах, и это три разные проблемы.
Запрос к базе. СКД генерирует один или несколько SQL-запросов и отправляет их СУБД. Если запрос плохо оптимизирован — большая часть времени уходит здесь. Это видно в SQL Profiler или в технологическом журнале как длительные DBMSSQL-события.
Компоновка результата. После того как СУБД вернула строки, СКД на сервере 1С формирует итоговую табличную модель: группирует, суммирует, накладывает условное оформление, рассчитывает иерархию. На больших наборах это тоже занимает время, особенно если в отчёте сложная иерархия групп и много рассчитываемых полей.
Вывод. Сформированный результат отправляется на клиент и рисуется в табличный документ. Если строк много (десятки тысяч) — рендеринг тоже может занимать секунды. Особенно при включённом условном оформлении: каждая ячейка проверяется на соответствие условиям.
Запустите замер производительности при формировании отчёта. Замер покажет, какая из трёх стадий съела время. Дальше — целитесь именно туда, а не «оптимизируете отчёт вообще».
Запрос медленный — что смотреть
В девяти случаях из десяти проблема в запросе. И первая ошибка, которую делают разработчики СКД, — пытаются оптимизировать схему компоновки в Конструкторе, не глядя на сам SQL, который улетает в СУБД.
Возьмите длительный запрос из ТЖ или Profiler. Это не запрос из Конструктора СКД — СКД его генерирует, добавляет условия отбора пользователя, ограничения по правам, сортировку. Финальный текст отличается от того, что вы видели в Конструкторе. И этот финальный текст нужно проанализировать.
Главное, что нужно проверить:
- Есть ли в запросе обращение к виртуальной таблице регистра без параметров — самая частая беда. Подробно эта тема разобрана в статье про параметры виртуальных таблиц 1С.
- Есть ли LEFT JOIN, у которого фильтр периода висит в WHERE, а не в условии соединения. Это разобрано в статье про WHERE vs ON в LEFT JOIN — и это вторая по частоте причина медленных СКД.
- Есть ли в запросе вычисляемые поля через подзапрос. Если в выводимых полях вы вычисляете что-то через
(ВЫБРАТЬ ... ИЗ ... ГДЕ ...)— это означает, что подзапрос выполняется на каждую строку результата. На десяти тысячах строк это десять тысяч подзапросов. - Есть ли соединения с большими таблицами без отбора. Если СКД соединяет регистр накопления с регистром сведений и оба не отфильтрованы по периоду — СУБД делает декартово произведение в память.
Возьмите подозрительный SQL и выполните его в SQL Server Management Studio с включённым планом выполнения. План покажет, где СУБД делает scan вместо seek, где временные таблицы свопятся на диск, где план выбран неоптимально. Часто план сразу подсвечивает узкое место красным.
Шесть приёмов оптимизации СКД
Приём 1: использовать параметры, а не отборы пользователя. В СКД есть две похожие концепции — параметры и отборы. Параметры жёстко связаны с запросом и применяются на стороне СУБД при формировании набора данных. Отборы пользователя применяются после того, как набор уже собран, на стороне 1С. Если в отчёте используется ограничение по периоду через отбор, СУБД сначала вытащит все строки за всё время, а потом 1С отфильтрует их в памяти. Если использовать параметр — СУБД отфильтрует сразу. На большом объёме разница в десятки раз.
Правильно: «Период» — это параметр запроса, «Подразделение» (которое пользователь иногда меняет) — может быть параметром, может быть отбором. Если поле часто фильтруется и имеет высокую селективность — делайте параметр.
Приём 2: предварительный набор данных через временную таблицу. Если основной запрос отчёта сложный, разбейте его на несколько шагов через временные таблицы. Сначала соберите ключевые ссылки в одну временную таблицу с минимальным набором полей. Потом во втором запросе соедините эту таблицу с регистрами и справочниками для получения подробностей. СУБД часто выбирает лучший план для двух простых запросов, чем для одного сложного.
В СКД временные таблицы создаются через шаги «Помещение в таблицу» в наборе данных, или через ручной запрос с пакетом.
Приём 3: вычисления — на стороне СКД, не в запросе. Если в выводимых полях нужно вычислить «Маржа = Сумма − Себестоимость», не делайте это в SQL. Создайте вычисляемое поле в схеме компоновки. СКД посчитает его в момент вывода, а не в запросе. Это разгружает СУБД и упрощает план.
Особенно это важно для агрегатов вроде «Среднее значение», «Процент от итога», «Накопительный остаток». Их вообще нельзя считать в запросе — для этого есть ресурсы и пользовательские поля СКД.
Приём 4: убрать лишние группировки и поля из настроек варианта. Часто бухгалтер настраивает вариант «как удобно посмотреть» и ставит группировку по контрагенту, по договору, по складу, по подразделению, по виду операции. Шесть уровней группировки для того, чтобы один раз посмотреть итог за месяц. СКД для каждого уровня группировки делает отдельный набор агрегаций — и каждый требует своих ресурсов.
Лечение — пересмотреть варианты отчёта. Если бухгалтер использует только два уровня группировки — оставьте два, остальные уберите. Это уменьшает время компоновки в разы.
Приём 5: ограничить количество строк через «Иерархия выводится с группировкой». Если отчёт возвращает десять тысяч строк деталей, и пользователь смотрит только итоги по группам — сделайте отображение только до уровня группировки, без детализации. Платформа всё равно не сможет нарисовать десять тысяч строк быстро. Если детали нужны иногда — оставьте их за «детальной расшифровкой» по щелчку.
Приём 6: сделать вариант «Свод» отдельно от варианта «Подробно». Один отчёт — два варианта. «Свод» работает по агрегированному запросу с группировкой на уровне СУБД. «Подробно» — по детальному, но с ограничением по периоду или другому критичному отбору. Это лучше, чем один «универсальный» вариант с десятью полями, который тормозит на всех режимах работы.
Когда дело не в запросе
Бывает, что замер показывает: запрос отрабатывает за полсекунды, а отчёт строится пять минут. Время уходит в компоновку или в вывод. Это другая категория проблем.
Тяжёлая иерархия группировок. Если в варианте отчёта пять уровней группировки, и каждая группа имеет несколько ресурсов — СКД для каждой строки деталей пройдёт по всем уровням и пересчитает все ресурсы. На десяти тысячах строк деталей и пяти уровнях группировки это получается несколько миллионов агрегаций. Это уже компоновка, и оптимизация запроса тут не поможет.
Условное оформление по сложным выражениям. Если в условном оформлении есть выражение вроде Выбор Когда Сумма > СредняяСумма Тогда ... Иначе ... Конец, и это применяется ко всем ячейкам — для каждой ячейки СКД пересчитывает выражение. На десятитысячном отчёте это занимает время. Лечение — упростить выражения, либо вынести индикатор в отдельное вычисляемое поле.
Большой объём результата. Если отчёт возвращает 100 тысяч строк, его невозможно нарисовать быстро. Тут только одно решение — не возвращать столько строк. Либо предложите пользователю сужать период, либо сделайте отбор обязательным. Никто не должен видеть 100 тысяч строк в табличном документе — это бессмысленно.
Подводный камень: один отчёт — разные пользователи
Если у вас отчёт, который под одним пользователем работает быстро, а под другим медленно, причина почти всегда в одном из двух мест.
Первое — RLS (ограничения доступа на уровне записей). Если у одного пользователя стоит роль с RLS, а у другого нет, в SQL-запрос для пользователя с RLS добавляется дополнительное условие. Это условие может полностью сломать план запроса, особенно если ограничение идёт через сложное соединение с справочником подразделений.
Лечение — посмотреть в ТЖ, какой именно SQL генерируется для проблемного пользователя. Сравнить с тем, что генерируется для пользователя без проблем. Разница — это и есть ваша точка приложения. Иногда RLS приходится переписывать (упрощать условие, выносить логику в отдельный регистр). Иногда — добавлять индекс по полю, которое участвует в условии RLS.
Второе — пользовательские настройки варианта. У каждого пользователя есть свой набор настроек: какие группировки, какие отборы, какие поля. Если бухгалтер сама поставила «Группировка по контрагенту, по договору, по подразделению, по виду операции» — у неё отчёт будет медленнее, чем у того, у кого осталась плоская группировка по умолчанию.
Лечение — открыть отчёт под учёткой бухгалтера, посмотреть её настройки, упростить или предложить альтернативный вариант с более лёгкой группировкой.
Алгоритм диагностики на каждый день
Когда вам приходит «отчёт тормозит», не нужно сразу копаться в схеме компоновки. Действуйте по шагам.
Шаг 1: воспроизведите проблему под собой. Если отчёт у вас работает быстро, а у пользователя — медленно, разница в данных, правах или настройках. Это сужает поиск.
Шаг 2: попросите пользователя открыть отчёт с замером производительности. Включите «Сервис → Замер производительности», нажмите «Сформировать», после построения выключите замер. Замер покажет, сколько времени съел каждый этап.
Шаг 3: если время съел запрос — включите ТЖ и достаньте текст SQL. Выполните SQL в SSMS с планом запроса. Найдите самый дорогой оператор. Это и есть точка для оптимизации.
Шаг 4: если время съела компоновка — посмотрите на сложность настроек варианта. Сколько группировок, сколько вычисляемых полей, сколько условного оформления. Упростите.
Шаг 5: если время съел вывод — оцените количество строк результата. Если их больше тысячи — нужно либо сужать период, либо менять вариант на сводный.
Этот алгоритм покрывает 95% случаев. Оставшиеся 5% — это сложные случаи с RLS, кросс-табами, динамическими фильтрами в коде формы. Они требуют отдельного разбора каждый раз, но они и встречаются редко.
Где обычно прячется медленность
За годы практики мы вывели одно простое наблюдение. Медленный СКД-отчёт почти всегда — это либо виртуальная таблица без параметров, либо LEFT JOIN с фильтром в WHERE, либо избыточные группировки в варианте. Эти три причины покрывают подавляющее большинство случаев.
Если вы первый раз диагностируете медленный отчёт, начните именно с этих трёх. В половине случаев первая же находка решает проблему. В остальных — даёт направление, в каком слое искать дальше.
И последнее. Не пытайтесь оптимизировать отчёт вслепую — без замера, без плана запроса, без понимания, где время. «Кажется, тут можно вынести» и «вроде это медленный запрос» — плохая основа для рефакторинга. Открыть SSMS и посмотреть план — это десять минут, которые экономят часы попыток наугад. После этого вы либо чините за полчаса, либо сразу видите, что задача не оптимизационная, а архитектурная.
СКД — это мощный инструмент, и почти любой медленный отчёт можно ускорить. Вопрос только в том, правильно ли вы поняли, где он медленный.


