Как вывести несколько значений табличной части в одну ячейку СКД без дублирования строк?

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

При разработке отчетов на системе компоновки данных (СКД) разработчики часто сталкиваются с классической задачей: необходимо вывести детальные записи по какому-либо объекту (например, по контрагенту) и в этой же строке, в отдельной колонке, отобразить данные из его табличной части (например, все его телефонные номера или адреса электронной почты). Если решать эту задачу «в лоб» через простое левое соединение в одном запросе, то итоговый результат вас разочарует. Строка контрагента размножится ровно столько раз, сколько записей контактной информации у него зарегистрировано. В итоге вместо аккуратного списка мы получаем огромную таблицу с дублирующимися данными.

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

Причина проблемы: почему левое соединение дублирует строки

Выясним причину такого поведения СУБД и СКД. Когда мы выполняем соединение таблицы контрагентов с табличной частью контактной информации в одном запросе, СУБД формирует декартово произведение подходящих строк. Для базы данных каждая запись контактной информации — это отдельная строка. СКД, получая такой плоский результат запроса, выводит детальные записи «как есть». Если у контрагента «Вектор» есть три телефона, то в отчете появится три строки «Вектор», в каждой из которых будет указан один из телефонов. Наша цель — объединить эти три телефона в одну строку через разделитель (например, точку с запятой) и оставить для контрагента ровно одну строку в отчете.

Рассмотрим по шагам основные способы решения этой задачи.

Решение 1. Использование встроенной функции СоединитьСтроки в ресурсах

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

Разберем по шагам, как это настроить:

  1. Оставьте левое соединение в вашем основном запросе. В качестве одного из полей выберите представление контактной информации (например, Представление из таблицы контактной информации).
  2. Перейдите на вкладку Ресурсы в схеме компоновки данных.
  3. Добавьте поле Представление в список ресурсов.
  4. В колонке Выражение для этого ресурса укажите формулу объединения:

СоединитьСтроки(Массив(Представление), "; ")

Рассмотрим подробнее, как работает это выражение. Функция Массив() собирает все детальные значения поля Представление, которые относятся к текущей группировке (в данном случае — к конкретному контрагенту), в один массив данных. Затем функция СоединитьСтроки берет этот массив и склеивает все его элементы в одну строку, используя указанный во втором параметре разделитель (в нашем примере это точка с запятой и пробел).

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

СоединитьСтроки(Массив(Различные Представление), "; ")

Благодаря этому нехитрому приему СКД автоматически «схлопнет» повторяющиеся телефоны или адреса прямо на этапе компоновки результата. Для этой задачи также подойдёт готовый пример отчета для сбора значений колонки из нескольких строк в СКД.

Решение 2. Архитектурный подход с разделением наборов данных

Хотя решение с левым соединением в одном запросе и использованием ресурсов работает хорошо, оно имеет существенный недостаток: запрос выбирает из базы данных избыточный объем информации, что может негативно сказаться на производительности при больших объемах данных. Более красивым и архитектурно правильным решением является разделение данных на два независимых набора.

Проанализируем эту ситуацию. Мы можем создать два набора данных-запроса и связать их на уровне СКД. В этом случае СКД самостоятельно выполнит оптимальное соединение и агрегацию строк.

Разберем алгоритм настройки:

  1. Создаем НаборДанных1 (основной запрос): Выбираем только контрагентов и их основные реквизиты. Никаких соединений с табличной частью контактной информации здесь делать не нужно. Пример запроса:
    
    ВЫБРАТЬ
        Контрагенты.Ссылка КАК Контрагент,
        Контрагенты.Код КАК Код,
        Контрагенты.ИНН КАК ИНН
    ИЗ
        Справочник.Контрагенты КАК Контрагенты
    
  2. Создаем НаборДанных2 (запрос по ТЧ): Выбираем данные контактной информации. Обязательно выбираем поле связи (владельца контактной информации). Пример запроса:
    
    ВЫБРАТЬ
        Контакты.Ссылка КАК Контрагент,
        Контакты.Вид КАК Вид,
        Контакты.Представление КАК Представление
    ИЗ
        Справочник.Контрагенты.КонтактнаяИнформация КАК Контакты
    
  3. Настраиваем связи наборов данных: Переходим на вкладку Связи наборов данных. Добавляем новую строку связи:
    • Источник связи: НаборДанных1
    • Приемник связи: НаборДанных2
    • Условие связи: Контрагент = Контрагент
    • Параметры: При необходимости можно передать параметры для оптимизации выборки во втором наборе.
  4. Настраиваем ресурсы: На вкладке Ресурсы добавляем поле Представление из второго набора данных с аналогичным выражением: СоединитьСтроки(Массив(Представление), "; ")

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

Решение 3. Объединение нескольких реквизитов из строки ТЧ

Зачастую перед разработчиком стоит более сложная задача: вывести в одну ячейку не просто плоский список значений одного поля, а красивую комбинацию из нескольких реквизитов строки табличной части. Например, мы хотим видеть не просто список телефонов, а строку вида: «Рабочий: +7...; Мобильный: +7...».

Если мы попробуем написать СоединитьСтроки(Массив(Вид + ": " + Представление)) в ресурсах, то СКД выдаст ошибку, так как она не умеет выполнять подобные операции со сложными выражениями внутри агрегатных функций. Посмотрим, как обойти это ограничение.

Способ А. Через Вычисляемое поле

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

  1. Перейдите на вкладку Вычисляемые поля.
  2. Добавьте новое поле с именем КонтактПодробно.
  3. В поле Выражение напишите логику объединения реквизитов одной строки: Строка(Вид) + ": " + Представление
  4. Перейдите на вкладку Ресурсы и сделайте это новое поле ресурсом, применив уже знакомую функцию соединения: СоединитьСтроки(Массив(КонтактПодробно), "; ")

Способ Б. Через функцию ТаблицаЗначений в ресурсах

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

  1. На вкладке Ресурсы для поля Представление напишите следующее выражение: СоединитьСтроки(ТаблицаЗначений(Вид, ": ", Представление), "; ")

В этом случае функция ТаблицаЗначений сформирует внутреннюю таблицу из колонок Вид и Представление, вставив между ними разделитель ": " (третий параметр функции), а затем СоединитьСтроки склеит полученные строки через точку с запятой. Это невероятно мощный инструмент, который экономит время и избавляет от необходимости создавать лишние вычисляемые поля.

Решение 4. Продвинутая группировка с помощью ВычислитьВыражениеСГруппировкойМассив

Иногда требуется не просто склеить все строки подряд, а предварительно сгруппировать их по определенному признаку внутри СКД. В сообщениях форума упоминается функция работы с группировками. Разберем работу функции ВычислитьВыражениеСГруппировкойМассив, которая идеально подходит для решения этой задачи.

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


СоединитьСтроки(ВычислитьВыражениеСГруппировкойМассив("Представление(Вид)", "Вид"), ", ")

Эта конструкция сначала выполнит внутреннюю группировку детальных записей по полю Вид, получит уникальный массив видов контактов и только потом объединит их в итоговую строку. Это позволяет гибко управлять выводом информации без усложнения исходных SQL-запросов к базе данных.

Решение 5. Интерактивная расшифровка объединенных значений

Серьезным минусом любого объединения строк в одну ячейку является потеря интерактивности отчета. Если пользователь дважды кликнет на объединенную строку контактов, платформа 1С не сможет определить, по какому именно контакту (или виду контакта) был произведен клик. Она предложит расшифровать всю строку целиком, что неудобно.

Рассмотрим продвинутый метод, который позволяет сохранить полную интерактивность и дать пользователю возможность «кликнуть» на конкретный элемент внутри объединенной ячейки:

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

Посмотрим на пример реализации обработчика расшифровки в модуле формы отчета:


&НаКлиенте
Процедура РезультатОбработкаРасшифровки(Элемент, Расшифровка, СтандартнаяОбработка)
    
    Перем ВыполненноеДействие, ПараметрыВыполнения;
    
    // Получаем данные расшифровки
    Данные = ПолучитьДанныеРасшифровки(Расшифровка);
    
    // Проверяем, что кликнули по нашему полю с массивом
    Если ТипЗнч(Данные) = Тип("Массив") Тогда
        СтандартнаяОбработка = Ложь;
        
        // Выводим пользователю список выбора элементов из массива
        СписокВыбора = Новый СписокЗначений;
        Для Каждого ЭлементМассива Из Данные Цикл
            СписокВыбора.Добавить(ЭлементМассива, Строка(ЭлементМассива));
        КонецЦикла;
        
        // Показываем меню выбора на экране
        ВыбранныйЭлемент = ВыбратьИзСписка(СписокВыбора, Элемент);
        Если ВыбранныйЭлемент <> Неопределено Тогда
            // Открываем форму выбранного объекта
            ПоказатьЗначение(, ВыбранныйЭлемент.Значение);
        КонецЕсли;
    КонецЕсли;
    
КонецПроцедуры

Такой подход делает ваш отчет не просто статичной таблицей, а мощным интерактивным инструментом бизнес-анализа, сохраняя при этом лаконичность и аккуратность визуального интерфейса. При необходимости программной обработки клика подойдёт пример отчета с получением значений группировок в расшифровке СКД.

Резюме

Мы детально изучили проблему дублирования строк при выводе табличных частей в отчетах СКД и разобрали несколько вариантов ее решения. Для простых задач идеально подойдет использование функции СоединитьСтроки в ресурсах СКД. Если отчет строится по сложным правилам или содержит большие объемы информации, лучшим выбором будет использование связей наборов данных. Для создания по-настоящему удобных и профессиональных отчетов не забывайте про возможности вычисляемых полей и переопределение расшифровки результатов.

← На главную