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