В процессе разработки сложных отчетов на системе компоновки данных (СКД) разработчики часто сталкиваются с необходимостью вывода специфических данных, которые трудно или неэффективно получать стандартным запросом. Популярным решением в таких случаях становится вызов экспортной функции общего модуля, которая возвращает объект типа ТаблицаЗначений, непосредственно внутри вычисляемого поля СКД.
Однако при реализации этого подхода возникают технические сложности: вместо ожидаемых данных в ячейках отчета отображается строка «Таблица значений», попытка расшифровки приводит к аварийному завершению работы клиента или ошибкам XDTO-сериализации, а производительность отчета падает из-за проблемы «N+1 запросов». В этой статье мы подробно разберем причины такого поведения платформы, научимся правильно обрабатывать таблицы значений в выражениях СКД и рассмотрим наиболее эффективный способ решения этой задачи с помощью связи наборов данных.
Когда вычисляемое поле СКД вызывает функцию общего модуля, возвращающую ТаблицаЗначений, система компоновки данных не может автоматически преобразовать многомерную структуру таблицы в плоский вид для вывода в одну ячейку табличного документа. По умолчанию платформа просто выводит строковое представление типа данных — «Таблица значений».
Попытка выполнить расшифровку (кликнуть по ячейке) или передать это поле дальше на клиентскую сторону вызывает ошибку преобразования XDTO. Это происходит потому, что объект ТаблицаЗначений не может быть сериализован для передачи между сервером и клиентом в контексте отображения данных формы. Чтобы избежать этих ошибок, необходимо преобразовать таблицу значений в примитивный тип (как правило, в строку) еще на этапе обработки данных в СКД.
Для работы с коллекциями в выражении вычисляемого поля СКД предусмотрены специальные встроенные функции. Рассмотрим их синтаксис и особенности работы:
ПолучитьЧасть(ТаблицаЗначений, НомераКолонок) — функция вычленяет из исходной таблицы значений только указанные колонки. Важно понимать, что на выходе она все еще возвращает объект типа ТаблицаЗначений, но уже с ограниченным набором колонок.СоединитьСтроки(ТаблицаЗначений, РазделительСтрок, РазделительКолонок) — функция агрегирует строки и колонки переданной таблицы значений (или элементы массива) в одну общую строку с использованием указанных разделителей.Проанализируем рабочее выражение для вычисляемого поля. Если нам необходимо получить данные только из четвертой колонки (индекс «4», содержащий, например, поле «Исполнитель») и вывести их через запятую, выражение в СКД будет выглядеть следующим образом:
СоединитьСтроки(ПолучитьЧасть(РаботаСВизамиСогласования.ПолучитьВизыДокумента(Ссылка), "4"), ", ", "; ")
Важная деталь: В функциях СКД индексы колонок передаются в виде строкового литерала в двойных кавычках (например, "4"). Индексация колонок в СКД начинается с 1.
Если мы хотим обращаться к колонке не по индексу, а по ее имени (например, "Исполнитель"), необходимо соблюдать правило явных псевдонимов в запросе внутри общего модуля. Если в тексте запроса функции поля выбираются без ключевого слова КАК (например, просто ВизыСогласования.Исполнитель), СКД может заменить их внутренними системными идентификаторами. Чтобы обращение по имени работало корректно, запрос в функции общего модуля должен быть написан так:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ВизыСогласования.Исполнитель КАК Исполнитель,
ВизыСогласования.РезультатСогласования КАК РезультатСогласования
ИЗ
Справочник.ВизыСогласования КАК ВизыСогласования
После этого в выражении вычисляемого поля можно уверенно использовать обращение по имени:
СоединитьСтроки(ПолучитьЧасть(РаботаСВизамиСогласования.ПолучитьВизыДокумента(Ссылка), "Исполнитель"), ", ", "; ")
Если выражения в вычисляемых полях СКД кажутся вам слишком громоздкими или их трудно отлаживать, хорошей альтернативой будет создание вспомогательной функции в общем модуле. Вместо того чтобы возвращать «тяжелую» таблицу значений и пытаться разобрать ее средствами СКД, функция общего модуля может сразу возвращать готовую отформатированную строку.
Рассмотрим пример реализации такой функции:
Функция ПолучитьИсполнителейДокументаСтрокой(Документ) Экспорт
ТаблицаВиз = ПолучитьВизыДокумента(Документ, Истина, Ложь, Ложь);
Если ТаблицаВиз.Количество() = 0 Тогда
Возврат "";
КонецЕсли;
МассивИсполнителей = ТаблицаВиз.ВыгрузитьКолонку("Исполнитель");
// Преобразуем элементы массива в строковые представления
МассивСтрок = Новый Массив;
Для Каждого ТекущийИсполнитель Из МассивИсполнителей Цикл
МассивСтрок.Добавить(Строка(ТекущийИсполнитель));
КонецЦикла;
Возврат СтрСоединить(МассивСтрок, ", ");
КонецФункции
В этом случае выражение в вычисляемом поле СКД становится максимально простым и лаконичным:
РаботаСВизамиСогласования.ПолучитьИсполнителейДокументаСтрокой(Ссылка)
Несмотря на то, что первые два решения полностью рабочие, они имеют критический недостаток — низкую производительность (проблема N+1 запросов). Если ваш отчет выводит 1000 документов, то для каждого документа (в каждой строке) будет выполнен отдельный запрос к базе данных из общего модуля. Это приведет к колоссальным задержкам при формировании отчета.
Чтобы отчет работал быстро, разберем по шагам, как перевести эту логику на механизм связей наборов данных СКД. Это позволит СУБД получить все данные пакетом за минимальное количество запросов.
НаборДанныхДокументы. В нем мы выбираем основную информацию о документах:
ВЫБРАТЬ
ВнутренниеДокументы.Ссылка КАК Ссылка,
ВнутренниеДокументы.Номер КАК Номер,
ВнутренниеДокументы.Дата КАК Дата
ИЗ
Справочник.ВнутренниеДокументы КАК ВнутренниеДокументы
НаборДанныхВизы. Он будет выбирать визы согласования для последующего объединения:
ВЫБРАТЬ
ВизыСогласования.Документ КАК Документ,
ВизыСогласования.Исполнитель КАК Исполнитель
ИЗ
Справочник.ВизыСогласования КАК ВизыСогласования
ГДЕ
ВизыСогласования.ПомещенаВИсторию = ЛОЖЬ
И ВизыСогласования.Удалена = ЛОЖЬ
И ВизыСогласования.ПометкаУдаления = ЛОЖЬ
НаборДанныхДокументыНаборДанныхВизыСсылка = ДокументИсполнитель в список ресурсов и напишем для него следующее выражение:
СоединитьСтроки(Массив(Различные Исполнитель), ", ")
Использование связей наборов данных позволяет не только избавиться от долгого формирования отчета, но и сохраняет все преимущества СКД: корректную работу группировок, итогов, отборов и условного оформления.