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