Как реализовать конкатенацию строк при группировке в запросе 1С?

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

В практике разработки на платформе 1С:Предприятие часто возникает задача, которую невозможно решить стандартными средствами языка запросов. Одна из таких задач — конкатенация (объединение) строк при группировке. Если для числовых данных мы можем использовать агрегатные функции СУММА или МАКСИМУМ, то для строковых полей встроенный SQL-движок 1С не предлагает прямого аналога функции типа string_agg или listagg.

Разберем подробно, как можно обойти это ограничение, используя Систему компоновки данных (СКД), программную постобработку и даже некоторые «хитрости» в самих запросах.

Решение через Систему компоновки данных (СКД)

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

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

  1. Создадим схему компоновки данных и добавим в нее набор данных-запрос. Запрос должен выбирать Контрагента и Комментарий без использования группировки СГРУППИРОВАТЬ ПО.
  2. Перейдем на вкладку «Ресурсы».
  3. Выберем поле Комментарий и в колонке «Выражение» напишем: СоединитьСтроки(Комментарий, ", ").
  4. В качестве рассчитываемых группировок укажем Контрагент.

Функция СоединитьСтроки обладает большой гибкостью. Проанализируем возможности её синтаксиса. Если нам нужно, чтобы комментарии внутри группы шли в определенном порядке (например, по дате), мы можем использовать вложенную функцию:

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

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

Программное получение результата СКД в таблицу значений

Часто бывает так, что результат объединения нужен не в печатной форме отчета, а непосредственно в коде (в виде ТаблицаЗначений). Разберем, как программно выполнить СКД и получить данные обратно. Посмотрим на пример кода, который эффективно решает эту задачу:


// Получаем макет СКД из отчета или обработки
ОбъектОтчет = РеквизитФормыВЗначение("Отчет");
ОСКД = ОбъектОтчет.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");

// Настраиваем компоновщик и параметры
НастройкиОСКД = ОСКД.НастройкиПоУмолчанию;
НастройкиОСКД.ПараметрыДанных.УстановитьЗначениеПараметра("СуммаМинимальная", 100);

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

// Процессор компоновки данных выполняет объединение
ПроцессорКомпоновкиОСКД = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиОСКД.Инициализировать(Макет);

// Выводим результат в Таблицу Значений
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений;
ТЗ = Новый ТаблицаЗначений;
ПроцессорВывода.УстановитьОбъект(ТЗ);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиОСКД);

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

Реализация конкатенации через постобработку кодом

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

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


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Т.Контрагент КАК Контрагент,
    |   Т.Комментарий КАК Комментарий
    |ИЗ
    |   РегистрСведений.Комментарии КАК Т";

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

// Используем Соответствие для быстрой группировки
СборкаДанных = Новый Соответствие;

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

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

Нестандартные методы: итерации в запросе

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

Рассмотрим пример логики: если у нас есть 1024 строки, то за 10 итераций парного соединения (1+2, 3+4...) мы можем объединить их в одну. Однако мы настоятельно не рекомендуем этот метод для реальных задач, так как он создает колоссальную нагрузку на SQL-сервер и крайне сложен в отладке. Подобные решения скорее являются «спортивным программированием», чем промышленным стандартом.

Работа на уровне СУБД (Direct SQL)

Если ваша конфигурация работает на Microsoft SQL Server 2017 или выше, на уровне СУБД доступна функция STRING_AGG. В случае использования PostgreSQL — функция string_agg. К сожалению, 1С не транслирует свои запросы в эти специфические функции. Однако при использовании внешних источников данных или прямых запросов (что требует осторожности и соблюдения лицензионности), эти функции позволяют решить задачу мгновенно.

Важный момент: при объединении строк всегда помните об ограничении длины. Если результат превысит максимально допустимый размер строки для выбранного типа данных в БД, произойдет обрезка или ошибка выполнения.

Современный подход: JSON и Массивы

Начиная с версии платформы 8.3.6, мы можем использовать сериализацию в JSON для передачи структурированных данных. Рассмотрим алгоритм:

  1. В СКД или через постобработку собираем данные в объект Массив.
  2. Сериализуем массив в строку JSON с помощью ЗаписатьJSON.
  3. Передаем эту строку в запросы или другие модули.

Хотя это не дает готовую строку «через запятую» сразу, этот метод позволяет передать неограниченное количество структурированных данных внутри одного поля запроса без потери информации.

Подведем итог: для большинства бизнес-задач оптимальным выбором будет программный вызов СКД с использованием функции СоединитьСтроки. Это решение наиболее сбалансировано с точки зрения производительности, читаемости кода и поддержки платформой.

← На главную