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

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

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

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

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

Если ваша задача заключается в выводе данных в отчет, то наиболее правильным и «штатным» способом будет использование Системы компоновки данных. Платформа 1С предоставляет мощную агрегатную функцию СоединитьСтроки(), которая работает на уровне ресурсов.

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

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

Рассмотрим возможности этой функции подробнее. Синтаксис функции выглядит следующим образом:

СоединитьСтроки(Выражение, Разделитель, Соединитель)

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


СоединитьСтроки(ВычислитьВыражениеСГруппировкойТаблицаЗначений("Различные Наименование"), ", ")

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

Способ 2. Программная обработка с использованием «СтрСоединить»

В ситуациях, когда результат нужен не в отчете, а в коде (например, для заполнения реквизита объекта или вывода сообщения пользователю), мы используем программный обход результата запроса. Начиная с версии платформы 8.3.6, в 1С появилась функция СтрСоединить(), которая работает значительно быстрее, чем ручная конкатенация через оператор «+».

Проанализируем пример кода, где мы получаем список ссылок и объединяем их в строку:


Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	ВариантыОбедов.Ссылка КАК Ссылка
|ИЗ
|	Справочник.ВариантыОбедов КАК ВариантыОбедов
|ГДЕ
|	НЕ ВариантыОбедов.ПометкаУдаления";

Результат = Запрос.Выполнить();

// Выгружаем колонку "Ссылка" сразу в массив
МассивСсылок = Результат.Выгрузить().ВыгрузитьКолонку("Ссылка");

// Преобразуем массив ссылок в массив строк (если необходимо)
// Для ссылок СтрСоединить сработает автоматически, вызывая строковое представление
ИтоговаяСтрока = СтрСоединить(МассивСсылок, ", ");

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

Способ 3. Оптимизация через объект «Соответствие» (Map)

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

Рассмотрим ситуацию: у нас есть запрос, который возвращает таблицу пар «Товар — Аналог». Нам нужно получить таблицу, где каждому товару соответствует одна строка со всеми его аналогами.


// 1. Получаем данные одним запросом
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

// 2. Используем Соответствие для группировки
ТаблицаГруппировки = Новый Соответствие;

Пока Выборка.Следующий() Цикл
    МассивАналогов = ТаблицаГруппировки.Получить(Выборка.Товар);
    Если МассивАналогов = Неопределено Тогда
        МассивАналогов = Новый Массив;
        ТаблицаГруппировки.Вставить(Выборка.Товар, МассивАналогов);
    КонецЕсли;
    МассивАналогов.Добавить(Строка(Выборка.Аналог));
КонецЦикла;

// 3. Формируем финальную таблицу
Для Каждого Элемент Из ТаблицаГруппировки Цикл
    Товар = Элемент.Ключ;
    СтрокаАналогов = СтрСоединить(Элемент.Значение, ", ");
    // Теперь у нас есть Товар и его склеенная строка
КонецЦикла;

Этот метод имеет линейную сложность O(n), что делает его максимально производительным при обработке больших массивов данных.

Способ 4. Метод последовательного обхода (линейный цикл)

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

Проанализируем алгоритм:

  1. В запросе обязательно указываем УПОРЯДОЧИТЬ ПО Товар.
  2. В цикле обхода выборки запоминаем значение товара из предыдущей итерации.
  3. Если товар в текущей строке совпадает с предыдущим — продолжаем накапливать строку.
  4. Если товар изменился — значит, формирование строки для предыдущего товара завершено.

Посмотрим на пример реализации:


Выборка = Запрос.Выполнить().Выбрать();
ТекущийТовар = Неопределено;
СтрокаАналогов = "";

Пока Выборка.Следующий() Цикл
    Если Выборка.Товар <> ТекущийТовар Тогда
        Если ТекущийТовар <> Неопределено Тогда
            // Сохраняем результат для предыдущего товара
            ЗаписатьРезультат(ТекущийТовар, СтрокаАналогов);
        КонецЕсли;
        
        ТекущийТовар = Выборка.Товар;
        СтрокаАналогов = Строка(Выборка.Аналог);
    Иначе
        СтрокаАналогов = СтрокаАналогов + ", " + Строка(Выборка.Аналог);
    КонецЕсли;
КонецЦикла;

// Не забываем обработать последний товар после завершения цикла
Если ТекущийТовар <> Неопределено Тогда
    ЗаписатьРезультат(ТекущийТовар, СтрокаАналогов);
КонецЕсли;

Сравнение методов и выводы

Выясним, какой метод выбрать в зависимости от ваших целей:

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

← На главную