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