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

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

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

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

Способ 1: Использование агрегатной функции СКД «СоединитьСтроки»

Это наиболее современный и рекомендуемый метод для отчетов, построенных на базе СКД (включая «Универсальный отчет»). Начиная с версии платформы 8.3.6, в СКД появилась встроенная функция СоединитьСтроки(), которая берет на себя всю рутину по обходу группировок.

Рассмотрим по шагам, как применить эту функцию:

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

Важный нюанс: Если мы хотим, чтобы это поле выводилось в итогах или в группировке более высокого уровня, нам нужно перейти на закладку «Ресурсы». Там мы выбираем наше вычисляемое поле и в качестве выражения ресурса снова указываем СоединитьСтроки(Наименование, ", "). Теперь при сворачивании данных по родителю, все наименования подчиненных элементов соберутся в одну красивую строку.

Способ 2: Использование функции «ВычислитьВыражениеСГруппировкойМассив»

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

Этот метод позволяет собрать все значения в массив прямо внутри контекста компоновки. Рассмотрим пример выражения:

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

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

Способ 3: Программная обработка результата запроса (через СтрСоединить)

Если задача стоит вне рамок СКД — например, в динамическом списке или при формировании текста для выгрузки в файл — мы используем программный обход результата запроса. Разберем по шагам наиболее производительный алгоритм:

Во-первых, не рекомендуется использовать конкатенацию строк (оператор +) в цикле, если количество итераций велико (как в примере автора с 40+ элементами). Это связано с особенностями выделения памяти под строки в 1С. Гораздо быстрее работает метод СтрСоединить.

Проанализируем пример кода:


// Предположим, РезультатЗапроса содержит колонку "Наименование"
ТаблицаДанных = РезультатЗапроса.Выгрузить();

// Собираем все значения в массив
МассивНаименований = ТаблицаДанных.ВыгрузитьКолонку("Наименование");

// Объединяем массив в одну строку с разделителем
ИтоговаяСтрока = СтрСоединить(МассивНаименований, ", ");

// Выводим результат
Сообщить(ИтоговаяСтрока);

Если нам нужно сгруппировать строки по какому-то признаку (например, собрать товары по складам), мы можем использовать следующий подход:


ТаблицаРезультат = Новый ТаблицаЗначений;
ТаблицаРезультат.Колонки.Добавить("Склад");
ТаблицаРезультат.Колонки.Добавить("СписокТоваров");

// Группируем данные программно
Для Каждого СтрокаСклад Из ТаблицаСкладов Цикл
    НоваяСтрока = ТаблицаРезультат.Добавить();
    НоваяСтрока.Склад = СтрокаСклад.Склад;
    
    // Отбираем товары этого склада из общей таблицы
    ПараметрыОтбора = Новый Структура("Склад", СтрокаСклад.Склад);
    СтрокиТоваров = ТаблицаВсехТоваров.НайтиСтроки(ПараметрыОтбора);
    
    МассивТоваров = Новый Массив;
    Для Каждого СтрТовар Из СтрокиТоваров Цикл
        МассивТоваров.Добавить(СтрТовар.Наименование);
    КонецЦикла;
    
    НоваяСтрока.СписокТоваров = СтрСоединить(МассивТоваров, "; ");
КонецЦикла;

Почему не стоит делать это «чистым» запросом?

Как упоминалось в обсуждении, технически соединение возможно через последовательные левые соединения (ЛЕВОЕ СОЕДИНЕНИЕ) таблицы самой с собой. Однако посмотрим на проблему реалистично: если элементов больше 5-10, текст запроса превращается в нечитаемую конструкцию. При 40 элементах, как у автора вопроса, количество соединений сделает запрос крайне тяжелым для оптимизатора СУБД, что приведет к существенным задержкам или даже падению процесса выполнения.

В 1С нет поддержки рекурсивных запросов (CTE), которые позволяют решать такие задачи в стандартном SQL. Поэтому попытка решить задачу «только в запросе» при переменном количестве строк — это путь к созданию непроизводительного кода.

Особенности Универсального отчета

Поскольку «Универсальный отчет» строится на механизме СКД, для добавления такого поля без изменения кода конфигурации можно попробовать использовать «Пользовательские поля». В режиме «1С:Предприятие» при настройке отчета выберите «Изменить вариант», перейдите на вкладку «Пользовательские поля» и создайте новое поле-выражение.

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

Выводы

Подводя итог, можно выделить следующие рекомендации:

Эти методы позволяют эффективно решать задачу объединения данных, сохраняя при этом высокую производительность системы и чистоту кода.

← На главную