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