Склейка текстовых полей в языке запросов 1С: оператор «+», ЕСТЬNULL, ВЫБОР и обработка результата в коде
Содержание
Строковые значения в запросе 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")). В запросе параметр уже будет строкой.Есть ли ограничение на длину результата склейки?
Можно ли использовать склеенное поле в ПОДОБНО?
"%" + Текст + "%", и передать его параметром. Не забудьте экранировать в пользовательском тексте символы % и _, иначе они сработают как шаблон.