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