В практике разработки на платформе 1С программисты часто сталкиваются с ситуацией, когда отчет, построенный на внешнем наборе данных (Таблице Значений), работает значительно медленнее, чем аналогичный отчет, данные для которого выбираются напрямую из базы (например, из регистра сведений). Особенно это заметно на этапе "вывода" отчета, когда прогресс-бар в строке состояния движется крайне медленно, хотя сама таблица-источник уже сформирована программно.
Разберем детально, почему так происходит, проанализируем внутренние механизмы Системы Компоновки Данных (СКД) и выясним, как оптимизировать процесс формирования отчета.
Это основная причина низкой производительности внешних наборов данных. Рассмотрим, как СКД работает с данными разных типов. Когда источником является Запрос к базе данных, платформа 1С на этапе генерации итогового SQL-запроса автоматически добавляет соединения (JOIN) к таблицам справочников или документов, чтобы получить текстовые представления (наименования, номера) для всех ссылочных полей. Это происходит одним пакетом на стороне сервера базы данных.
В случае с внешним набором данных (ТЗ) ситуация иная:
Это порождает классическую проблему N+1 запросов: вместо одного эффективного запроса система выполняет тысячи мелких обращений к БД в цикле. Даже наличие кэширования в современных версиях платформы 1С 8.3 не спасает на больших объемах данных, так как накладные расходы на вызов каждого запроса остаются существенными.
Проанализируем разницу в объеме обрабатываемой информации. Когда источником данных является объект БД (регистр), механизм СКД выполняет оптимизацию запроса: из базы выбираются только те поля, которые реально используются в текущих настройках компоновки (выбранные поля, отборы, группировки).
При использовании ВнешнегоНабораДанных вы передаете в ПроцессорКомпоновкиДанных всю таблицу целиком. Если в вашей ТЗ 23 колонки и 50 000 строк, все они будут прокачаны через память сервера приложений и внутренние структуры СКД, даже если пользователь в настройках отчета выбрал всего 2 колонки. Копирование больших объемов данных из памяти 1С во внутренние объекты компоновки — ресурсоемкая операция.
Многие разработчики пытаются ускорить вывод, добавляя индексы в ТаблицаЗначений перед ее передачей в СКД. Однако, как показывает практика и тесты, это практически не влияет на скорость вывода компоновки. Выясним почему: ПроцессорКомпоновкиДанных не использует индексы вашей ТаблицыЗначений напрямую. При получении данных система строит собственные внутренние индексы в оперативной памяти для выполнения операций группировки и соединений. Поэтому время, затраченное на индексацию ТЗ в коде, часто оказывается просто потерянным.
Самый эффективный способ ускорить вывод ТЗ в СКД — избавить систему от необходимости ходить в базу за представлениями. Для этого мы можем заранее подготовить текстовые описания объектов. Рассмотрим, как это реализовать по шагам:
ТаблицеЗначений добавим дополнительные колонки строкового типа для всех ссылочных полей (например, НоменклатураПредставление).ПРЕДСТАВЛЕНИЕ(Поле).Номенклатура) установим свойство "Выражение представления", указав в нем имя нашей новой строковой колонки.Пример подготовки таблицы в коде:
// Подготовим таблицу с дополнительной колонкой для текстового представления
ТЗДанных = Новый ТаблицаЗначений;
ТЗДанных.Колонки.Добавить("Товар", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
ТЗДанных.Колонки.Добавить("ТоварПредставление", Новый ОписаниеТипов("Строка"));
// После заполнения данными (ссылками) заполним представления одним запросом
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка, ПРЕДСТАВЛЕНИЕ(Ссылка) КАК Текст ИЗ Справочник.Номенклатура ГДЕ Ссылка В (&МассивСсылок)";
// ... (логика пакетного заполнения ТЗДанных.ТоварПредставление)
В настройках поля Товар в СКД в поле "Выражение представления" пишем: ТоварПредставление. Теперь СКД просто возьмет готовую строку из таблицы, не обращаясь к базе данных.
Проанализируем типы колонок в вашей ТЗ. Если колонка имеет составной тип или тип ЛюбаяСсылка, СКД тратит дополнительное время на определение метаданных при обработке каждой ячейки. Посмотрим на пример правильного создания колонок:
// Плохо:
ТЗ.Колонки.Добавить("Документ"); // Тип не определен, будет "Произвольный"
// Хорошо:
МассивТипов = Новый Массив;
МассивТипов.Добавить(Тип("ДокументСсылка.РеализацияТоваровУслуг"));
ОписаниеТиповСтр = Новый ОписаниеТипов(МассивТипов);
ТЗ.Колонки.Добавить("Документ", ОписаниеТиповСтр);
Чем строже определены типы данных во внешнем наборе, тем быстрее процессор компоновки сможет их типизировать во внутренних структурах.
Поскольку СКД не может наложить отбор ГДЕ на уровне получения данных из внешнего источника (она всегда получает ТЗ целиком и фильтрует ее уже в памяти), необходимо максимально ограничить объем передаваемой таблицы еще в коде. Разберем ситуацию: если пользователь установил отбор по организации в настройках отчета, лучше всего прочитать этот отбор из настроек компоновки и применить его к запросу, который формирует вашу ТаблицуЗначений, прежде чем передавать ее в СКД.
Если скорость вывода критична и данных много, использование промежуточного регистра сведений (как сделал автор темы) является оправданным архитектурным решением. Это позволяет переложить всю тяжелую работу по сортировке, группировке и получению представлений на SQL-сервер. Однако, если использование регистра невозможно, применение "Выражений представления" и строгая типизация колонок ТЗ позволят сократить время формирования отчета в несколько раз, приблизив его к скорости работы стандартных запросов.