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