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