Почему зависает отчет СКД при добавлении ресурса и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Каждый разработчик 1С рано или поздно сталкивается с загадочной ситуацией: сложный аналитический запрос моментально выполняется в обычной консоли запросов, но стоит перенести его в Систему компоновки данных (СКД) и добавить хотя бы одно числовое поле в список ресурсов, как формирование отчета намертво зависает. На первый взгляд кажется, что суммирование колонки в итоговой таблице — это простейшая операция, которая не должна занимать много времени. Однако под капотом платформы 1С происходят сложные процессы трансформации запросов. В этой статье мы подробно разберем, почему добавление ресурса может превратить быстрый запрос в источник бесконечных зависаний, и научимся решать эту проблему.

Выясняем причину: как СКД оптимизирует наши запросы

Начнем с главного секрета Системы компоновки данных: запрос, который вы написали в конструкторе СКД, и запрос, который платформа реально отправляет на СУБД — это два абсолютно разных запроса. СКД обладает мощным встроенным оптимизатором. Если в настройках отчета (на вкладке «Компоновка данных» или в пользовательских настройках на форме) не выбраны определенные поля, СКД просто выкидывает их из итогового SQL-запроса.

Более того, платформа умеет исключать целые таблицы, присоединенные через левое соединение (LEFT JOIN), если из этих таблиц не выбирается ни одно поле. Именно поэтому в «пустом» отчете (без ресурсов и детальных полей) всё летает за пару секунд: СУБД физически не обращается к тяжелым связанным таблицам. Но как только мы добавляем поле в ресурсы (например, рассчитываем по нему функцию СУММА), СКД обязана рассчитать итоги по этому полю. В этот момент оптимизатор принудительно возвращает в запрос все связанные таблицы и вычисления, что мгновенно нагружает СУБД.

Быстрый тест с помощью роли «Обязательное»

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

  1. Откроем схему компоновки данных нашего отчета.
  2. Перейдем на вкладку «Наборы данных».
  3. Найдем в таблице полей то числовое поле, при добавлении которого в ресурсы начинаются тормоза.
  4. В колонке «Роль» откроем свойства этого поля и установим флажок «Обязательное».
  5. Уберем это поле из списка ресурсов СКД и запустим отчет.

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

Глубинные технические причины падения производительности

Рассмотрим подробнее, какие именно архитектурные изменения происходят в запросе при добавлении ресурса и почему СУБД (будь то MS SQL Server или PostgreSQL) перестает справляться с нагрузкой.

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


SELECT
    T1.Field1,
    SUM(T1.ResourceField)
FROM 
    (SELECT ... Исходный запрос с соединениями ...) КАК T1
GROUP BY 
    T1.Field1

Для сложных запросов с объединениями (ОБЪЕДИНИТЬ) или группировками внутри самого текста запроса такая многоуровневая вложенность мешает оптимизатору СУБД построить правильный план выполнения. СУБД может отказаться от использования подходящих индексов и выбрать неоптимальный метод соединения таблиц (например, соединение петлей Nested Loops вместо быстрого Hash Match), из-за чего время выполнения вырастает в сотни раз.

2. Смещение отборов во внешнее условие (WHERE / HAVING). При использовании автозаполнения СКД пытается оптимизировать наложение отборов, пропихивая их внутрь параметров виртуальных таблиц (например, в параметры среза последних или оборотов регистров). Однако, когда вы добавляете поле в ресурсы, логика применения пользовательских отборов может измениться. Отбор может сместиться из параметров виртуальной таблицы в общую секцию ГДЕ (WHERE) или даже ИМЕЮЩИЕ (HAVING). В итоге СУБД сначала выберет миллионы записей из базы, рассчитает по ним ресурс, и только потом отсечет ненужные строки.

3. Расчет ресурсов на стороне сервера приложений 1С. Если суммируемое поле является вычисляемым полем СКД или в выражении его ресурса используется функция общего модуля (например, ИмяМодуля.МояФункция(Поле)), СУБД не может выполнить эти расчеты. В таком случае СКД вынуждена отказаться от группировки на уровне базы данных. Она выполняет детальный запрос, выкачивает сотни тысяч строк в оперативную память сервера приложений 1С, последовательно в цикле рассчитывает функцию для каждой строки и только потом производит суммирование. Это приводит к колоссальным зависаниям и ошибкам нехватки памяти.

4. Проблема дублирования данных и нагрузка на TempDB. Если в запросе СКД используется связь «один ко многим» (например, соединение шапки документа с табличной частью), строки детальных записей дублируются. При добавлении ресурса разработчики часто пытаются обойти проблему задвоения сумм с помощью сложных агрегатных функций, таких как Количество(Различные...) или ВычислитьВыражениеСГруппировкойМассив. Это заставляет СУБД создавать временные таблицы в базе данных TempDB для расчета уникальных значений, что критически замедляет работу всей системы.

Пошаговое руководство по исправлению зависаний

Разберем по шагам, как переработать схему компоновки данных и вернуть скорость формирования отчета к считанным секундам.

Шаг 1: Использование временных таблиц вместо сложных соединений

Избегайте громоздких левых соединений с подзапросами в основном тексте запроса СКД. Вместо этого вынесите тяжелые выборки во временные таблицы и обязательно проиндексируйте их по полям связи. Пакетные запросы оптимизируются платформой 1С гораздо лучше. Посмотрим на правильный пример структуры запроса:


ВЫБРАТЬ
    ПродажиОбороты.Контрагент КАК Контрагент,
    ПродажиОбороты.СуммаОборот КАК СуммаОборот
ПОМЕСТИТЬ ВТ_Продажи
ИЗ
    РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода) КАК ПродажиОбороты
ИНДЕКСИРОВАТЬ ПО
    Контрагент
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Продажи.Контрагент,
    ВТ_Продажи.СуммаОборот,
    Партнеры.КодПоставщика КАК КодПоставщика
ИЗ
    ВТ_Продажи КАК ВТ_Продажи
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Партнеры КАК Партнеры
    ПО ВТ_Продажи.Контрагент = Партнеры.Ссылка

Шаг 2: Настройка групп для левых соединений

Если в вашем запросе присутствует несколько левых соединений, подскажите оптимизатору СКД, как их правильно отключать. Для этого:

  1. Откройте конструктор запроса в СКД.
  2. Перейдите на вкладку «Компоновка данных», а затем на вкладку «Таблицы».
  3. Для присоединенных таблиц снимите флажок «Обязательная».
  4. Назначьте им разные номера групп в колонке «Группа». Это позволит СКД гибко исключать из итогового SQL-запроса только те соединения, поля которых не задействованы в текущих настройках отчета.

Шаг 3: Анализ реального SQL-запроса

Не гадайте на кофейной гуще. Используйте специализированные инструменты для просмотра реального запроса, который СКД отправляет на СУБД. Вы можете воспользоваться штатной «Консолью компоновки данных» (доступна на ИТС) или профессиональным инструментом из пакета «Инструменты разработчика».

Запустите отчет в консоли компоновки, получите макет компоновки для выполнения и скопируйте итоговый текст запроса. Запустите его в обычной консоли запросов 1С или проанализируйте его план выполнения в СУБД. Вы сразу увидите, какое именно соединение или вложенный запрос вызывают «узкое горлышко» производительности.

← На главную