Как объединить текстовые строки из нескольких строк при группировке в 1С без использования СКД?

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

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

В этой статье мы подробно разберем, как обойти это ограничение, проанализируем различные подходы и выберем наиболее эффективные способы реализации склейки строк вне системы компоновки данных.

Метод 1. Использование функции СтрСоединить и объекта ТаблицаЗначений

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

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

Порядок действий:

  1. Выполним запрос без группировки (или с частичной группировкой).
  2. Выгрузим результат в ТаблицаЗначений.
  3. Сгруппируем данные программно, используя метод ВыгрузитьКолонку() и СтрСоединить().

Посмотрим на пример кода:


// Предположим, РезультатЗапроса содержит колонки "Контрагент" и "НомерТелефона"
ТЗ = РезультатЗапроса.Выгрузить();
ТЗ_Итог = ТЗ.СкопироватьКолонки();

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

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

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

Этот метод значительно быстрее, чем конкатенация строк через оператор "+", так как СтрСоединить выделяет память под итоговую строку один раз.

Метод 2. Обход выборки с использованием итогов (ИТОГИ ПО)

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

Выясним, как это работает. В тексте запроса мы указываем ключевое поле в секции ИТОГИ ПО. При обходе результата мы получаем доступ к детальным записям каждой группы.

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


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

Результат = Запрос.Выполнить();
ВыборкаКонтрагент = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

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

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

Метод 3. Программный вызов движка СКД

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

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

Пример настройки ресурса в схеме:

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

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

Метод 4. Решение внутри запроса для фиксированного набора строк

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

Проанализируем ситуацию на примере соединения имен сотрудников в отделе:

  1. Создаем временную таблицу с нумерованными строками внутри группы (через соединение таблицы самой с собой по условию Т1.Ссылка > Т2.Ссылка).
  2. Выполняем несколько левых соединений основной таблицы с этой временной таблицей по номеру строки (1, 2, 3...).
  3. В итоговом поле ВЫБРАТЬ используем конструкцию:

ЕСТЬNULL(Т1.Имя, "") + 
ВЫБОР КОГДА Т2.Имя ЕСТЬ NULL ТОГДА "" ИНАЧЕ ", " + Т2.Имя КОНЕЦ + 
ВЫБОР КОГДА Т3.Имя ЕСТЬ NULL ТОГДА "" ИНАЧЕ ", " + Т3.Имя КОНЕЦ

Внимание: этот метод крайне громоздкий, трудночитаемый и не масштабируется. Мы рекомендуем использовать его только в исключительных случаях, когда обработка результата на стороне встроенного языка по каким-то причинам невозможна.

Резюме и рекомендации

Мы рассмотрели четыре способа решения задачи объединения строк. Подведем итоги и выберем оптимальный путь:

Помните, что отсутствие функции СУММА() для строк в запросах 1С — это осознанное ограничение платформы, направленное на сохранение идентичности поведения запросов на разных типах систем управления базами данных (MSSQL, PostgreSQL, Oracle, IBM DB2).

← На главную