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

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

При программном формировании отчетов на базе системы компоновки данных (СКД), особенно при использовании связок из нескольких схем или передаче данных через внешние наборы (таблицы значений), разработчики нередко сталкиваются с исключением: «Ошибка компоновки макета по причине: Поле не найдено [ИмяПоля]». Разберем по шагам, почему возникает эта ошибка, где сохраняются конфликтные поля и как правильно выстроить работу со структурой настроек СКД.

Анализ проблемы: откуда берется отсутствующее поле

Рассмотрим типовой сценарий: в первой схеме или отдельным запросом формируется таблица значений, после чего ее колонки динамически добавляются в коллекцию полей набора данных второй схемы компоновки данных:


КоллекцияПолей = СхемаКомпоновкиДанных.НаборыДанных[0].Поля;
КоллекцияПолей.Очистить();

Для Каждого Колонка Из ТЗ_.Колонки Цикл
    ПолеНабора = КоллекцияПолей.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
    ПолеНабора.ПутьКДанным = Колонка.Имя;
    ПолеНабора.Поле        = Колонка.Имя;
КонецЦикла;

При первой попытке вывести отчет все отрабатывает штатно. Однако стоит пользователю или коду установить отбор, как генератор макета выбрасывает исключение с указанием поля, которого вроде бы нет в текущей выборке (например, Организация при группировке по предприятию).

Выясним причину такого поведения. Генератор макета компоновки анализирует не только текущий состав полей набора данных, но и все примененные настройки. Ошибка возникает тогда, когда в настройках отчета присутствует обращение к полю, отсутствующему в схеме. Чаще всего поле «застревает» в следующих местах:

  1. Коллекция отборов схемы по умолчанию: если вторая схема была загружена из макета через ПолучитьМакет(), в ее исходных настройках могли остаться предопределенные отборы.
  2. Пользовательские настройки: компоновщик настроек накладывает КомпоновщикНастроек.ПользовательскиеНастройки поверх основных. Если пользователь ранее устанавливал фильтр по полю Организация, этот элемент сохраняется в пользовательских настройках даже после программного изменения колонок.
  3. Отборы внутри структуры: условия фильтрации могут находиться не на верхнем уровне отчета, а внутри конкретных группировок, строк, колонок или точек/серий диаграммы (Структура).
  4. Порядок, условное оформление и выбранные поля: если поле было удалено из набора данных, но осталось в сортировке или оформлении, компоновщик также выдаст ошибку отсутствия поля.

Способ 1. Очистка отборов и сброс настроек

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


// Очистка отборов верхнего уровня
КомпоновщикНастроек.Настройки.Отбор.Элементы.Очистить();
КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Очистить();

// Если используется диаграмма или вложенные группировки, обходим структуру
Для Каждого ЭлементСтруктуры Из КомпоновщикНастроек.Настройки.Структура Цикл
    ЭлементСтруктуры.Отбор.Элементы.Очистить();
КонецЦикла;

Если требуется полностью привести настройки в соответствие с обновленной схемой данных, правильнее всего выполнить реинициализацию компоновщика с новым источником доступных настроек:


// Обновляем доступные настройки на основе измененной схемы
ИсточникНастроек = Новый ИсточникДоступныхНастроекКомпоновкиДанных(СхемаКомпоновкиДанных);
КомпоновщикНастроек.Инициализировать(ИсточникНастроек);

// Загружаем чистые настройки схемы
КомпоновщикНастроек.ЗагрузитьНастройки(СхемаКомпоновкиДанных.НастройкиПоУмолчанию);

Способ 2. Корректное добавление полей во внешний набор

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


Для Каждого Колонка Из ТЗ_.Колонки Цикл
    ПолеНабора = КоллекцияПолей.Добавить(Тип("ПолеНабораДанныхСхемыКомпоновкиДанных"));
    ПолеНабора.ПутьКДанным = Колонка.Имя;
    ПолеНабора.Поле        = Колонка.Имя;
    ПолеНабора.Заголовок   = Колонка.Имя;
    ПолеНабора.ТипЗначения = Колонка.ТипЗначения;
КонецЦикла;

Оптимизация производительности: почему связка «Запрос -> ТЗ -> Внешний набор» работает медленно

Нередко к двухэтапной схеме прибегают в надежде ускорить расчет отчета. Однако архитектурно такой подход часто дает обратный эффект по следующим причинам:

  1. Отключение внутренних оптимизаций СКД: выполняя запрос вручную и сгружая результат в ТаблицаЗначений, мы лишаем СКД возможности модифицировать текст запроса. Механизм фигурных скобок {ГДЕ ...} перестает автоматически отсекать лишние записи на уровне базы данных, и в память сервера выгружается избыточный объем строк.
  2. Дублирование данных в оперативной памяти: сначала вся выборка хранится в объекте ТаблицаЗначений, затем передается в ВнешниеНаборыДанных, после чего процессор компоновки снова копирует и трансформирует данные для построения группировок.
  3. Перенос нагрузки с СУБД на сервер 1С: группировки, вычисление итогов и сортировка по внешнему набору данных выполняются силами платформы 1С без использования индексов СУБД, что на больших выборках работает значительно медленнее, чем прямой запрос к таблицам базы.

Для ускорения работы рекомендуется переносить промежуточные вычисления в единый пакетный запрос внутри одного набора данных НаборДанныхЗапрос, используя менеджер временных таблиц непосредственно в СКД, либо обязательно индексировать колонки таблицы значений перед передачей во внешний набор:


// Индексация полей, по которым строятся группировки, точки диаграммы или накладываются отборы
ТЗ_.Индексы.Добавить("Предприятие");
← На главную