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