Склейка текстовых полей в языке запросов 1С: оператор «+», ЕСТЬNULL, ВЫБОР и обработка результата в коде

· 9 мин

Содержание

Строковые значения в запросе 1С складываются оператором +: Контрагенты.Наименование + ", ИНН " + Контрагенты.ИНН. Если хотя бы одно слагаемое равно NULL, весь результат станет NULL, поэтому поля из левого соединения оборачивайте в ЕСТЬNULL(Поле, "").

Числа, даты и ссылки к строке оператором + не прибавляются — запрос упадёт с ошибкой несовместимых типов. Их удобнее склеить уже в коде, после выполнения запроса. Там же через СтрСоединить() собирают в одну строку значения из многих записей: в языке запросов агрегатной функции для склейки строк нет.

Всё ниже относится к платформе 8.3 и не зависит от конфигурации: одинаково работает в Бухгалтерии, ЗУП, УТ и в самописных базах.

Оператор «+»: базовая склейка строк в запросе

Оператор + применим к любым выражениям строкового типа: полям, строковым литералам, параметрам и результатам строковых функций.

ВЫБРАТЬ
	Контрагенты.Наименование + " (ИНН " + Контрагенты.ИНН + ")" КАК Представление,
	Контрагенты.Ссылка КАК Ссылка
ИЗ
	Справочник.Контрагенты КАК Контрагенты
ГДЕ
	НЕ Контрагенты.ЭтоГруппа

На выходе получится колонка вида ООО Ромашка (ИНН 7701234567).

Что ещё можно складывать:

  • 📌 Поле с параметром — &Префикс + Номенклатура.Артикул. Параметр задаётся строкой в коде через УстановитьПараметр().
  • 📌 Поле через точку — Реализация.Контрагент.Наименование + " / " + Реализация.Договор.Наименование. Платформа сама построит соединение с таблицами справочников.
  • 📌 Результат строковой функции — ПОДСТРОКА(Поле, 1, 10) + "…".

Кавычки, когда текст запроса записан в коде

В конструкторе запросов строковый литерал пишется в обычных кавычках: " (ИНН ". Когда тот же текст вставлен в модуль как строка встроенного языка, каждая кавычка удваивается:

Запрос.Текст =
"ВЫБРАТЬ
|	Контрагенты.Наименование + "" (ИНН "" + Контрагенты.ИНН + "")"" КАК Представление
|ИЗ
|	Справочник.Контрагенты КАК Контрагенты";

Конструктор запросов делает это удвоение сам, если открыть его из модуля. При ручной правке текста про удвоение часто забывают, и модуль перестаёт компилироваться.

Перевод строки внутри результата

Литерала Символы.ПС в языке запросов нет. Перенос передают параметром:

Запрос.УстановитьПараметр("ПС", Символы.ПС);
// в тексте запроса:
// Контрагенты.Наименование + &ПС + Контрагенты.ЮридическийАдрес КАК Шапка

Так же передают табуляцию (Символы.Таб) и любые другие служебные символы.

NULL при сложении строк: почему пропадает весь текст

NULL появляется в полях из левого соединения, когда в правой таблице нет пары. Сложение с NULL даёт NULL, и в отчёте вместо «Иванов И. И., отдел продаж» вы увидите пустую ячейку.

ВЫБРАТЬ
	Сотрудники.Наименование + ", " + ЕСТЬNULL(Подразделения.Наименование, "без подразделения") КАК Подпись
ИЗ
	Справочник.Сотрудники КАК Сотрудники
		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПодразделенияОрганизаций КАК Подразделения
		ПО Сотрудники.ТекущееПодразделение = Подразделения.Ссылка

Имена реквизитов в примере условные: в ЗУП 3.1 подразделение сотрудника хранится не в справочнике, а в регистре кадровой истории. Смысл примера — в ЕСТЬNULL, а не в структуре таблиц.

Правило простое: каждое слагаемое, которое может прийти из левого или полного соединения, оборачивайте в ЕСТЬNULL. Поля основной таблицы NULL не содержат: у незаполненного строкового реквизита значение — пустая строка.

Отдельный случай — реквизит составного типа, который может быть и строкой, и ссылкой. Строку из него достают так: ВЫРАЗИТЬ(Поле КАК СТРОКА(150)). Если в записи хранится не строка, ВЫРАЗИТЬ вернёт NULL, поэтому и здесь нужен ЕСТЬNULL.

Числа, даты и ссылки рядом со строкой

Самая частая причина ошибки при конкатенации в запросе — попытка прибавить к тексту не строку:

СлагаемоеЧто получится с +Как поступить
Число (Сумма, Количество)Ошибка несовместимых типовСклеить в коде через Формат() или СтрШаблон()
Дата (Дата документа)ОшибкаСклеить в коде: Формат(Дата, "ДФ=dd.MM.yyyy")
Ссылка (Контрагент)ОшибкаВзять строковый реквизит: Контрагент.Наименование
Строка неограниченной длиныСклеится, но результат тоже неограниченныйОбрезать: ПОДСТРОКА(Комментарий, 1, 300)

Классический ВЫРАЗИТЬ(... КАК СТРОКА(N)) приводит к строке только строковые значения, в том числе из составного типа. Число или дату он в текст не превращает.

Номер документа строковый, его можно складывать. Дату документа — нет. Поэтому подпись «Реализация № 123 от 05.03» собирают после запроса:

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Подпись = СтрШаблон("Реализация № %1 от %2 на сумму %3",
		Выборка.Номер,
		Формат(Выборка.Дата, "ДФ=dd.MM.yyyy"),
		Формат(Выборка.СуммаДокумента, "ЧДЦ=2; ЧГ=0"));
КонецЦикла;

СтрШаблон() есть в платформе с версии 8.3.6. Формат ЧГ=0 убирает пробелы-разделители групп разрядов, иначе сумма 15000 выведется как «15 000».

Лишние пробелы: обрезка до и после склейки

Пробелы в результате появляются по двум причинам:

  • ✂️ Реквизит фиксированной длины. В конфигураторе у строкового реквизита можно выбрать допустимую длину «Фиксированная». Такое значение дополняется пробелами до полной длины, и после него в склейке остаётся «хвост».
  • ✂️ Пробелы, введённые пользователем. Наименование «Ромашка␣» с пробелом в конце даёт двойной пробел перед следующим слагаемым.

В платформе 8.3.20 и новее в язык запросов добавлены строковые функции, среди них СОКРЛ, СОКРП и СОКРЛП:

ВЫБРАТЬ
	СОКРЛП(Товары.Артикул) + " " + СОКРЛП(Товары.Наименование) КАК ПолноеИмя
ИЗ
	Справочник.Номенклатура КАК Товары

Обрезайте каждое слагаемое, а не готовую строку: СОКРЛП над итогом уберёт только края, а двойной пробел в середине останется.

Если база работает на платформе старше 8.3.20 или режим совместимости конфигурации ниже, новые функции в запросе недоступны. Тогда обрезайте в коде функцией встроенного языка СокрЛП() при обходе выборки.

Номер версии платформы и режим совместимости — разные вещи. Новая платформа не включает новые функции запросов, если в свойствах конфигурации стоит старый режим совместимости. Проверьте оба значения: версию — в окне «О программе», режим — в свойствах корня конфигурации в конфигураторе.

Склейка по условию: ВЫБОР КОГДА

Когда часть текста нужна не всегда, используйте ВЫБОР. Типовая задача — не выводить «КПП» у индивидуальных предпринимателей, у которых он пустой:

ВЫБРАТЬ
	Контрагенты.Наименование
		+ ", ИНН " + Контрагенты.ИНН
		+ ВЫБОР
			КОГДА Контрагенты.КПП = ""
				ТОГДА ""
			ИНАЧЕ ", КПП " + Контрагенты.КПП
		КОНЕЦ КАК Реквизиты
ИЗ
	Справочник.Контрагенты КАК Контрагенты

Ветка ИНАЧЕ обязательна по смыслу. Без неё ВЫБОР вернёт NULL во всех случаях, не подошедших под КОГДА, и весь результат обнулится.

Условия можно комбинировать через И, ИЛИ, НЕ:

ВЫБОР
	КОГДА Контрагенты.КПП <> "" И НЕ Контрагенты.ЭтоГруппа
		ТОГДА " / КПП " + Контрагенты.КПП
	ИНАЧЕ ""
КОНЕЦ

Пример целиком: строка «покупатель — договор — подразделение»

Ниже приёмы из предыдущих разделов собраны в одном запросе. Договор может быть не указан, подразделение приходит из левого соединения, у ИП нет КПП:

ВЫБРАТЬ
	Реализация.Ссылка КАК Документ,
	Реализация.Номер КАК Номер,
	Реализация.Дата КАК Дата,
	Реализация.Контрагент.Наименование
		+ ВЫБОР
			КОГДА Реализация.Контрагент.КПП = ""
				ТОГДА ""
			ИНАЧЕ " (КПП " + Реализация.Контрагент.КПП + ")"
		КОНЕЦ
		+ ", договор: " + ЕСТЬNULL(Реализация.ДоговорКонтрагента.Наименование, "не указан")
		+ ", " + ЕСТЬNULL(Подразделения.Наименование, "без подразделения") КАК Описание
ИЗ
	Документ.РеализацияТоваровУслуг КАК Реализация
		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПодразделенияОрганизаций КАК Подразделения
		ПО Реализация.ПодразделениеОрганизации = Подразделения.Ссылка
ГДЕ
	Реализация.Дата МЕЖДУ &НачалоПериода И &КонецПериода

Номер и дату здесь выбираем отдельными колонками. Подпись «№ … от …» собирают уже в коде через СтрШаблон() и Формат(), как показано выше. ЕСТЬNULL стоит и на договоре: при пустой ссылке обращение через точку к её реквизиту даёт NULL. Имена реквизитов документа сверьте со своей конфигурацией в конфигураторе.

Для двух-трёх вариантов ВЫБОР читается нормально. Если веток больше пяти, текст запроса становится трудно сопровождать — такую логику проще вынести в код.

Несколько записей в одну строку: СтрСоединить после запроса

Частый вопрос — как получить «Товар А, Товар Б, Товар В» одной ячейкой. В языке запросов 1С нет аналога STRING_AGG из SQL: агрегатные функции СУММА, МАКСИМУМ и другие со строками в такой роли не работают.

Решение — выгрузить колонку и склеить её функцией СтрСоединить() (есть с версии 8.3.6):

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Товары.Номенклатура.Наименование КАК Наименование
|ИЗ
|	Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
|	Товары.Ссылка = &Документ
|УПОРЯДОЧИТЬ ПО
|	Товары.НомерСтроки";
Запрос.УстановитьПараметр("Документ", Документ);

Наименования = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Наименование");
Перечень = СтрСоединить(Наименования, ", ");

ВыгрузитьКолонку() возвращает массив, а СтрСоединить() склеивает его элементы через разделитель. Разделитель после последнего элемента не ставится, лишнюю запятую обрезать не нужно.

Если нужна склейка по группам (у каждого документа — свой перечень товаров), используйте выборку с группировкой: Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) и внутренний цикл по деталям. В каждой группе накапливайте массив и вызывайте СтрСоединить() перед переходом к следующей группе.

Не собирайте длинную строку через Итог = Итог + ", " + Значение в цикле на тысячи строк. Каждая итерация создаёт новую строку, и время растёт заметно. Массив и один вызов СтрСоединить() работают быстрее.

Что быстрее: в запросе или в коде

Сама склейка дешёвая и там, и там. Скорость теряется на другом:

Где склеиваетеКогда уместноЧем грозит
В поле ВЫБРАТЬГотовая колонка для отчёта или СКДПочти ничем при разумной длине строк
В условии ГДЕПрактически никогдаИндекс не используется, СУБД сканирует таблицу
В СГРУППИРОВАТЬ ПОЕсли группировка действительно идёт по составному текстуДлинные ключи группировки, лишняя нагрузка на СУБД
В коде после запросаЧисла, даты, перечни из многих строкВремя на обход выборки, на сотнях тысяч строк заметно

Главная ловушка — сравнение со склеенным выражением:

// медленно: индекс по полям не используется
ГДЕ Товары.Артикул + Товары.Наименование = &Строка

// быстро: каждое поле сравнивается отдельно
ГДЕ Товары.Артикул = &Артикул
	И Товары.Наименование = &Наименование

Разбейте строку поиска на части в коде и передайте их отдельными параметрами.

Со строками неограниченной длины (например, Комментарий) в запросе нельзя группировать, сортировать и сравнивать. Если такое поле попало в склейку, результат тоже станет неограниченным, и УПОРЯДОЧИТЬ ПО по нему не сработает. Ограничьте длину: ПОДСТРОКА(Документы.Комментарий, 1, 300).

Ошибки при конкатенации строк в запросе

Диагностика: выберите симптом

Частые вопросы

Можно ли склеить строки в схеме компоновки данных, не трогая запрос?
Да. В СКД есть вычисляемые поля, и в их выражении тоже работает оператор +. Это удобно, когда запрос общий для нескольких вариантов отчёта.
Как сложить строку и параметр-число?
Преобразуйте число в строку в коде до передачи: Запрос.УстановитьПараметр("Номер", Формат(Номер, "ЧГ=0")). В запросе параметр уже будет строкой.
Есть ли ограничение на длину результата склейки?
Длина результата складывается из длин слагаемых. При больших длинах результат может оказаться неограниченным, а с такими строками нельзя сортировать и группировать.
Можно ли использовать склеенное поле в ПОДОБНО?
Проще и надёжнее собрать шаблон в коде, например "%" + Текст + "%", и передать его параметром. Не забудьте экранировать в пользовательском тексте символы % и _, иначе они сработают как шаблон.
Что выбрать для отчёта: склейку в запросе или в макете?
Если колонка нужна для сортировки или отбора — в запросе. Если только для вывода — в макете или в вычисляемом поле СКД: запрос останется проще.