Рассмотрим классическую и весьма неприятную ситуацию, с которой часто сталкиваются разработчики 1С. Имеется отчет, построенный на Системе Компоновки Данных (СКД) без программного вывода, использующий сложный запрос (например, содержащий около десятка пакетов и временных таблиц). В консоли запросов или на тестовой базе этот отчет формируется за считанные секунды. Однако на рабочей базе процесс зависает на сообщении «Вывод отчета» и может длиться десятки минут. Самое странное в этой ситуации то, что SQL-профайлер при этом молчит — складывается ощущение, что запрос вообще не уходит на сервер СУБД.
Проанализируем эту ситуацию и разберем по шагам, почему платформа «застревает» на этапе генерации макета компоновки или подготовки процессора вывода, и как это можно исправить.
Выясним причину, по которой поведение на тестовой и рабочей базах кардинально отличается. Зачастую на тестовой базе разработчик запускает отчет под пользователем с полными правами, тогда как на рабочей базе формирование происходит под профилем с ограниченными правами и включенными проверками RLS на уровне записей.
Когда СКД компонует макет (за это отвечает объект КомпоновщикМакетаКомпоновкиДанных), она пытается автоматически встроить условия RLS во все используемые временные таблицы и соединения. Если временных таблиц много (например, 12 пакетов), этот процесс может критически перегрузить процессор сервера 1С. Алгоритм оптимизации платформы может уйти в бесконечный цикл или вызвать утечку памяти. В итоге до СУБД запрос попросту не доходит.
Что делать: Протестируйте выполнение отчета на рабочей базе под пользователем с полными правами. Если отчет формируется быстро, причину нужно искать в сложных условиях RLS. В качестве решения можно оптимизировать сами роли или перенести формирование данных отчета в привилегированный общий модуль.
Посмотрим на пример работы СКД со ссылочными полями. Если в вашем запросе выбираются ссылки (на справочники, документы и т.д.), а в макете СКД настроен вывод их текстовых свойств (наименований), платформа может не добавлять получение представлений в сам SQL-запрос, а попытаться получить их «на лету» во время вывода.
На тестовой базе, где объем данных невелик, это не вызывает проблем. Но на рабочей базе, при выводе десятков тысяч строк, платформа начинает генерировать тысячи микро-запросов к СУБД для получения каждого наименования. В SQL-профайлере это может выглядеть как «пустота», если ваши фильтры настроены на отлов только долгих запросов (от 100 мс и выше), в то время как микро-запросы выполняются за 1 мс, но их количество «вешает» систему.
Разберем специфику работы компоновщика макета с временными таблицами. При генерации итогового запроса СКД анализирует, какие поля реально выводятся пользователем, и пытается выкинуть неиспользуемые соединения. Если запрос состоит из множества пакетов, а в настройках набора данных включено автозаполнение, на некоторых версиях платформы 1С алгоритм оптимизации начинает сбоить.
Также обратите внимание на ручной макет оформления (если он используется). Если в макете присутствуют объединенные ячейки с включенным свойством автоширины или автовысоты, процессор вывода табличного документа начинает выполнять тяжелые геометрические вычисления для каждой выводимой строки. На больших объемах данных это намертво вешает поток вывода на клиенте или сервере.
Посмотрим, как правильно вычленить проблемный этап. Формирование отчета состоит из нескольких шагов. Нам нужно программно измерить время каждого из них:
КомпоновщикМакета.Выполнить() — именно здесь СКД генерирует финальный текст запроса и связывает параметры.ПроцессорКомпоновкиДанных).ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент).Если зависание происходит на первом этапе, проблему нужно искать в настройках СКД, RLS или багах платформы при работе с большим количеством временных таблиц. Если на третьем — виноват сложный макет или получение представлений.
Если стандартный вывод в ТабличныйДокумент зависает из-за внутренней логики платформы, и оптимизация макета не помогает, мы применим классический и проверенный подход — выгрузим данные в ТаблицаЗначений, а затем самостоятельно выведем их пользователю или передадим СКД готовый набор данных.
Рассмотрим пример кода, который позволяет обойти зависание процессора вывода, сгенерировав плоскую таблицу значений:
// Получаем настройки и параметры отчета
Настройки = КомпоновщикНастроек.ПолучитьНастройки();
// Инициализируем компоновщик макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
// Важно: используем генератор макета для коллекции значений!
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, , ,
Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений"));
// Создаем и инициализируем процессор компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки);
// Создаем процессор вывода в таблицу значений
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТаблицаРезультат = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТаблицаРезультат);
// Запускаем вывод (выполняется в оперативной памяти без отрисовки геометрии ячеек)
ПроцессорВывода.Вывести(ПроцессорКомпоновки);
Полученную таблицу ТаблицаРезультат можно далее передать в качестве внешнего набора данных в другую (более простую) схему СКД для финального оформления, либо программно вывести в ТабличныйДокумент с помощью простых циклов. Этот метод полностью исключает из цепочки сложный платформенный процессор вывода макетов табличных документов, оставляя только быструю работу 1С с оперативной памятью, что в 99% случаев решает проблему «зависания» отчета на больших объемах данных.