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

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

При работе с системой "1С:Предприятие" часто возникает задача представить данные из табличной части справочника или другого объекта в виде одной агрегированной строки. Например, если у вас есть справочник "Спецификации" с табличной частью "Компоненты", и вы хотите вывести в отчете название спецификации вместе со списком всех ее компонентов в одной строке, как "Пирожки с луком и яйцом (Тесто дрожжевое, Яйца вареные, Лук порей)". Эта задача особенно актуальна при построении отчетов на Системе Компоновки Данных (СКД), где требуется гибкое форматирование вывода без написания сложного кода.

Рассмотрим подробнее несколько подходов к решению этой проблемы, акцентируя внимание на наиболее эффективных методах для СКД.

Решение 1: Использование функции СоединитьСтроки() в СКД

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

Давайте разберем по шагам, как применить функцию СоединитьСтроки() в вашей схеме компоновки данных:

  1. Откройте схему компоновки данных (СКД)

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

  2. Добавьте вычисляемое поле

    На вкладке "Вычисляемые поля" вашей схемы компоновки данных создайте новое вычисляемое поле. Дайте ему осмысленное имя, например, "КомпонентыСтрокой" или "ПолноеНаименованиеСпецификации".

  3. Определите выражение для вычисляемого поля

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

    • Значение: Это выражение, которое указывает, какие данные из табличной части необходимо объединить. Для сбора всех значений из определенного столбца табличной части мы обычно используем агрегатную функцию Массив(). Например, если в запросе табличная часть называется СпецификацииКомпоненты и у нее есть поле Наименование, то Значение будет выглядеть как Массив(СпецификацииКомпоненты.Наименование).

      Важно: Преобразование типов. Если поля табличной части имеют нестроковый тип (например, ссылка на другой справочник, число или дата), их необходимо предварительно преобразовать в строку. Для ссылочных типов используйте функцию Представление() (например, Массив(Представление(СпецификацииКомпоненты.СсылкаНаКомпонент))). Для числовых или строковых значений можно использовать функцию Формат() (например, Массив(Формат(СпецификацииКомпоненты.Количество, "ЧЦ=15; ЧДЦ=3"))).

    • РазделительЭлементов: Это строка, которая будет использоваться для разделения каждого элемента в итоговой объединенной строке. Чаще всего используется ", " для разделения запятой и пробелом. Вы также можете использовать ", " + Символы.ПС, если хотите, чтобы каждый компонент выводился с новой строки.

    • РазделительКолонок: Этот параметр используется, когда в Значение передается таблица значений (например, Массив(Таблица.Поле1, Таблица.Поле2)). В нашем случае, когда мы объединяем значения из одного столбца табличной части, этот параметр обычно остается пустым: "".

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

    СоединитьСтроки(Массив(СпецификацииКомпоненты.Наименование), ", ", "")

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

    
    Спецификации.Наименование + " (" + СоединитьСтроки(Массив(СпецификацииКомпоненты.Наименование), ", ", "") + ")"
    

    Где Спецификации - это алиас вашего справочника "Спецификации" в запросе.

  4. Настройте группировку в отчете

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

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

    Ваше вычисляемое поле "КомпонентыСтрокой" должно быть выведено на уровне этой группировки. Таким образом, для каждой уникальной спецификации (по которой идет группировка) функция СоединитьСтроки() соберет все компоненты из ее табличной части.

Преимущества использования СоединитьСтроки() в СКД:

Ограничения и нюансы:

Решение 2: Предварительная запись данных в отдельный реквизит справочника

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

Давайте разберем по шагам, как реализовать этот подход:

  1. Добавьте новый реквизит в справочник

    В конфигураторе откройте ваш справочник "Спецификации". Добавьте новый строковый реквизит, например, "КомпонентыСтрокой". Установите для него свойство "Длина" в "0" (неограниченная длина), чтобы он мог хранить длинные строки.

  2. Реализуйте заполнение реквизита при записи элемента

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

    
    // Пример кода в модуле объекта справочника "Спецификации"
    // Этот код формирует строку из наименований компонентов
    // и записывает ее в новый реквизит "КомпонентыСтрокой".
    // Предполагается, что табличная часть называется "Компоненты".
    
    Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
        // Инициализируем пустую строку для компонентов
        СтрокаКомпонентов = "";
        Разделитель = ""; // Переменная для управления разделителями
    
        // Перебираем все строки табличной части "Компоненты"
        Для Каждого КомпонентТЧ Из Компоненты Цикл
            // Добавляем разделитель перед каждым компонентом, кроме первого
            СтрокаКомпонентов = СтрокаКомпонентов + Разделитель + КомпонентТЧ.Наименование;
            // Устанавливаем разделитель для следующего компонента
            Разделитель = ", ";
        КонецЦикла;
    
        // Формируем итоговую строку, включая наименование спецификации
        // и список компонентов в скобках.
        ЭтотОбъект.КомпонентыСтрокой = ЭтотОбъект.Наименование + " (" + СтрокаКомпонентов + ")";
    
    КонецПроцедуры
    

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

  3. Обновите существующие данные (инициализация)

    Как указано в исходной задаче, если у вас уже есть более 50,000 элементов справочника, необходимо обновить их, чтобы новый реквизит "КомпонентыСтрокой" был заполнен. Это можно сделать с помощью внешней обработки. Такая обработка должна пройти по всем элементам справочника, получить их объекты и программно записать. При каждой записи будет срабатывать обработчик ПередЗаписью, который мы реализовали на предыдущем шаге.

    
    // Пример кода для внешней обработки или обработки в консоли запросов/кода
    // Этот код проходит по всем элементам справочника "Спецификации"
    // и вызывает метод "Записать()" для каждого.
    
    Сообщить("Начало обновления спецификаций...");
    
    КоличествоОбновлено = 0;
    ВыборкаСпецификаций = Справочники.Спецификации.Выбрать();
    Пока ВыборкаСпецификаций.Следующий() Цикл
        Попытка
            СпецификацияОбъект = ВыборкаСпецификаций.ПолучитьОбъект();
            // Вызов Записать() инициирует выполнение обработчика ПередЗаписью
            СпецификацияОбъект.Записать();
            КоличествоОбновлено = КоличествоОбновлено + 1;
            Если КоличествоОбновлено % 100 = 0 Тогда // Сообщаем о прогрессе каждые 100 элементов
                Сообщить("Обновлено: " + КоличествоОбновлено + " спецификаций.");
            КонецЕсли;
        Исключение
            Сообщить("Ошибка при обновлении спецификации " + ВыборкаСпецификаций.Наименование + ": " + ОписаниеОшибки());
        КонецПопытки;
    КонецЦикла;
    
    Сообщить("Обновление завершено. Всего обновлено: " + КоличествоОбновлено + " спецификаций.");
    

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

  4. Использование реквизита в запросах/СКД

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

Преимущества предварительной записи данных:

Недостатки предварительной записи данных:

Какой вариант выбрать?

Выбор между этими двумя подходами зависит от ваших конкретных требований и приоритетов:

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

← На главную