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