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