Как соединить значения из нескольких строк в одну строку через запятую в запросе 1С?

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

В процессе разработки на платформе 1С:Предприятие часто возникает задача преобразования данных из вертикального вида в горизонтальный. Классический пример: имеется список товаров и их свойств (например, цвета), и нам необходимо вывести название товара, а рядом — все его цвета в одной строке через запятую. Напрямую в языке запросов 1С (в отличие от некоторых диалектов SQL, таких как PostgreSQL или MS SQL) функции типа STRING_AGG не существует. Однако это ограничение легко обходится несколькими способами.

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

Решение 1: Использование Системы компоновки данных (СКД)

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

Шаг 1: Настройка схемы

Допустим, наш запрос в наборе данных выглядит стандартно:


ВЫБРАТЬ
    СправочникТовары.Наименование КАК Товар,
    СправочникЦвета.Наименование КАК Цвет
ИЗ
    Справочник.Товары КАК СправочникТовары
    ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Цвета КАК СправочникЦвета
    ПО СправочникТовары.Ссылка = СправочникЦвета.Владелец

Шаг 2: Настройка ресурсов

Для того чтобы система объединила строки, необходимо перейти на закладку «Ресурсы» в схеме компоновки данных:

  1. Выбираем поле Цвет.
  2. В колонке «Выражение» пишем: СоединитьСтроки(Цвет, ", ").
  3. В колонке «Рассчитывать по...» указываем Товар.

Шаг 3: Программный вызов СКД

Если вам нужно не просто вывести отчет, а получить эту строку для вставки в документ (как обсуждалось в сообщении 2), мы можем программно исполнить схему и выгрузить результат в таблицу значений. Проанализируем пример кода:


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

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

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

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

// Теперь в ТаблицаРезультат лежат свернутые строки с запятыми

Решение 2: Программный обход выборки с использованием Итогов

Если использование СКД кажется избыточным или требуется максимальная скорость работы в коде модуля, мы применим обход результата запроса с группировкой по родительскому объекту. Для этого в языке запросов существует предложение ИТОГИ. Выясним, как это работает на практике.

Разберем алгоритм: мы группируем данные по товару, а затем внутри каждой группы товаров перебираем все детальные записи (цвета) и склеиваем их в переменную.


ТекстЗапроса = 
"ВЫБРАТЬ
|   Товар,
|   Цвет
|ИЗ
|   ВТ_Данные
|ИТОГИ ПО
|   Товар";

Запрос = Новый Запрос(ТекстЗапроса);
РезультатЗапроса = Запрос.Выполнить();

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

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

Этот метод считается наиболее производительным при обработке больших массивов данных для записи в объекты базы данных, так как он использует индексированные итоги запроса.

Решение 3: «Грязный хак» через левые соединения (только для малого количества строк)

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

Внимание: Мы не рекомендуем использовать этот метод для 10 и более строк (как упоминал автор в сообщении 3), так как это катастрофически замедлит запрос и сделает его нечитаемым.


ВЫБРАТЬ
    Т1.Товар,
    Т1.Цвет + 
    ВЫБОР КОГДА Т2.Цвет ЕСТЬ NULL ТОГДА "" ИНАЧЕ ", " + Т2.Цвет КОНЕЦ +
    ВЫБОР КОГДА Т3.Цвет ЕСТЬ NULL ТОГДА "" ИНАЧЕ ", " + Т3.Цвет КОНЕЦ КАК ВсеЦвета
ИЗ
    Таблица Т1
    ЛЕВОЕ СОЕДИНЕНИЕ Таблица Т2 ПО Т1.Товар = Т2.Товар И Т1.Цвет <> Т2.Цвет
    ЛЕВОЕ СОЕДИНЕНИЕ Таблица Т3 ПО Т1.Товар = Т3.Товар И Т1.Цвет <> Т3.Цвет И Т2.Цвет <> Т3.Цвет
// Данный метод требует сложной фильтрации дублей и порядковых номеров

Как мы видим, этот путь крайне тернист. Поэтому лучше вернуться к Решению 1 или Решению 2.

Важные технические нюансы

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

1. Ограничение длины строки

В языке запросов 1С при работе со строками неограниченной длины (тип Строка без указания длины) многие операции могут быть недоступны. Если ваше поле Цвет имеет неограниченную длину, обязательно используйте функцию ВЫРАЗИТЬ. Посмотрим на пример:

ВЫРАЗИТЬ(Цвет КАК СТРОКА(100))

2. Порядок сортировки в СКД

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

3. Почему нет аналога в языке запросов?

Многих разработчиков интересует вопрос: почему платформа 1С до сих пор не реализовала конкатенацию строк при группировке в обычном запросе? Причина кроется в кроссплатформенности. 1С работает с MS SQL Server, PostgreSQL, IBM DB2 и Oracle. В каждой из этих СУБД механизмы агрегации строк реализованы по-разному. Чтобы обеспечить идентичное поведение системы на любой базе данных, 1С выносит логику склейки строк на уровень приложения (в СКД или программный код).

Подведем итоги

Мы рассмотрели основные способы соединения строк в одну в контексте 1С. Для формирования отчетов и сложных выгрузок оптимальным выбором будет СКД с её функцией СоединитьСтроки(). Для быстрой обработки данных «на лету» в коде модуля лучше всего подходит запрос с итогами и последующий цикл. Выбирайте метод, исходя из ваших задач, и помните о производительности системы!

← На главную