Как объединить строки в запросе 1С, если одно из полей имеет неограниченную длину?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок в среде 1С:Предприятие программисты регулярно сталкиваются с необходимостью объединения (конкатенации) нескольких строковых полей в одно результирующее поле прямо в тексте запроса. Типичный пример — формирование красивого представления паспортных данных физического лица, где нужно объединить вид документа, его серию, номер и поле ДокументКемВыдан.

Однако при попытке выполнить простое сложение строк через оператор + в запросе часто возникает критическая ошибка системы. Давайте проанализируем причину этой проблемы и подробно разберем все надежные способы ее решения.

Выясняем причину: почему запрос выдает ошибку?

В платформе 1С строковые типы данных делятся на две категории: строки ограниченной длины (длина которых жестко задана в конфигураторе, например, Строка(100)) и строки неограниченной длины. На уровне СУБД (будь то Microsoft SQL Server, PostgreSQL или IBM DB2) строки неограниченной длины хранятся в специальных типах данных для больших объектов (LOB), таких как nvarchar(max) или text.

Язык запросов 1С спроектирован с учетом строгой типизации и переносимости между различными СУБД. Прямая конкатенация обычных строк со строками неограниченной длины (в нашем примере это реквизит ДокументКемВыдан) запрещена на уровне СУБД или не поддерживается транслятором запросов 1С. Попытка выполнить сложение напрямую приводит к ошибке несовместимости типов данных.

Для решения этой задачи нам необходимо предварительно преобразовать строку неограниченной длины в строку фиксированной длины внутри запроса либо вынести логику склейки на уровень сервера приложений. Рассмотрим все доступные варианты. Для решения смежных проблем со сравнением неограниченных полей и ошибками несовместимых типов при приведении можно использовать руководство по сравнению строк с представлением ссылки в запросе.

Способ 1. Использование конструкции ВЫРАЗИТЬ

Самый очевидный и часто используемый способ решить проблему непосредственно в запросе — принудительно ограничить длину проблемного поля. Для этого в языке запросов 1С предусмотрена специальная конструкция ВЫРАЗИТЬ.

Давайте разберем по шагам, как изменить исходный запрос автора темы. Мы приведем неограниченное поле ДокументКемВыдан к строке фиксированной длины, например, 150 символов.

Посмотрим на пример практической реализации:


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

Этот запрос отработает без ошибок. Однако при использовании конструкции ВЫРАЗИТЬ крайне важно помнить о двух технических ограничениях:

  1. Лимит в 1024 символа: Максимальная длина, которую можно указать внутри конструкции ВЫРАЗИТЬ(... КАК СТРОКА(N)), строго ограничена 1024 символами. Если вы укажете 1025 или больше, платформа выдаст синтаксическую ошибку еще на этапе компиляции запроса.
  2. Проблема дополнения пробелами в MS SQL Server: Если ваша база данных работает под управлением MS SQL Server, использование ВЫРАЗИТЬ приведет к тому, что возвращаемая строка будет принудительно дополнена пробелами справа до указанной длины (в нашем примере — до 150 символов). Если вы попытаетесь склеить такое поле с последующим текстом, между ними образуется огромный пустой промежуток. Чтобы избежать этого, итоговую строку в коде 1С придется дополнительно обрабатывать функцией СокрЛП().

Способ 2. Использование функции ПОДСТРОКА как альтернативы

Иногда разработчики вместо функции ВЫРАЗИТЬ применяют встроенную функцию запроса ПОДСТРОКА. Она также позволяет получить ограниченный строковый фрагмент из поля неограниченной длины.

Синтаксис функции выглядит следующим образом: ПОДСТРОКА(<ИсходнаяСтрока>, <НачальнаяПозиция>, <Длина>).

Рассмотрим пример запроса с использованием этой функции:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ПаспортныеДанные.ДокументВид.Наименование КАК ВидДокумента,
    |    ПОДСТРОКА(ПаспортныеДанные.ДокументКемВыдан, 1, 150) КАК КемВыдан,
    |    ПаспортныеДанные.ДокументВид.Наименование + "" "" + ПОДСТРОКА(ПаспортныеДанные.ДокументКемВыдан, 1, 150) КАК ОднойСтрокой
    |ИЗ
    |    РегистрСведений.ПаспортныеДанныеФизЛиц КАК ПаспортныеДанные";

Главный плюс функции ПОДСТРОКА по сравнению с ВЫРАЗИТЬ заключается в том, что она возвращает реальную строку без принудительного заполнения правыми пробелами до максимального лимита. Это избавляет от необходимости последующей зачистки строки.

Тем не менее, этот метод имеет серьезные скрытые недостатки, из-за которых фирма 1С официально не рекомендует использовать ПОДСТРОКА для приведения типов:

Важный нюанс: обработка значений NULL

Независимо от того, какой способ ограничения строки в запросе вы выберете, существует опасность получить неожиданный пустой результат (значение NULL) при объединении строк. Если хотя бы одно из полей, участвующих в конкатенации, окажется пустым (например, у физлица не заполнен вид документа или поле кем выдан отсутствует в базе), то результат всего сложения строк превратится в NULL.

Чтобы защитить запрос от этой проблемы, каждый строковый операнд перед объединением обязательно нужно оборачивать в защитную функцию ЕСТЬNULL, заменяя потенциальный NULL на пустую строку "".

Давайте посмотрим на пример полностью безопасного и оптимизированного запроса:


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

Способ 3. Перенос склейки строк на уровень сервера приложений (постобработка)

Давайте проанализируем архитектуру вашего решения. Всегда ли нужно склеивать строки именно на уровне базы данных (в СУБД)? На самом деле, операции со строками — это одна из самых "тяжелых" задач для реляционных баз данных. Если вам нужно просто вывести объединенную строку на экран или в печатную форму, гораздо эффективнее и правильнее перенести эту логику на уровень сервера приложений 1С.

Первый вариант постобработки — использование обычной таблицы значений в коде. Мы выбираем поля из базы данных как есть, а затем программно обходим строки и формируем результат: такой метод подготовки данных для сопоставления объектов эффективен при использовании обработки для переноса данных между похожими конфигурациями 1С, работающей с произвольными полями поиска.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |    ПаспортныеДанные.ДокументВид.Наименование КАК ВидДокумента,
    |    ПаспортныеДанные.ДокументКемВыдан КАК КемВыдан
    |ИЗ
    |    РегистрСведений.ПаспортныеДанныеФизЛиц КАК ПаспортныеДанные";

ТаблицаДанных = Запрос.Выполнить().Выгрузить();
ТаблицаДанных.Колонки.Добавить("ОднойСтрокой", Новый ОписаниеТипов("Строка"));

Для Каждого СтрокаДанных Из ТаблицаДанных Цикл
    // Программная склейка на сервере приложений 1С работает очень быстро
    // и автоматически корректно обрабатывает строки любой длины без обрезки
    СтрокаДанных.ОднойСтрокой = СокрЛП(СтрокаДанных.ВидДокумента) + " " + СокрЛП(СтрокаДанных.КемВыдан);
КонецЦикла;

Способ 4. Реализация склейки строк средствами СКД

Если вы разрабатываете отчет с использованием Системы компоновки данных (СКД), то выполнять склейку строк в запросе — это заведомо проигрышный вариант. СКД предоставляет колоссальные возможности для работы со строковыми данными без каких-либо ограничений на длину.

Рассмотрим два основных механизма СКД для решения нашей задачи:

1. Вычисляемые поля СКД

Выражения вычисляемых полей СКД выполняются не на уровне базы данных SQL, а непосредственно на сервере приложений 1С. Благодаря этому вы можете безболезненно использовать стандартный оператор + для сложения любых строк, включая строки неограниченной длины. При этом вам не нужно заботиться о приведении типов и функциях ВЫРАЗИТЬ.

Вы можете добавить вычисляемое поле на вкладке "Вычисляемые поля" в схеме компоновки данных:

2. Использование функций общих модулей в СКД

В выражениях вычисляемых полей СКД разрешено напрямую вызывать экспортные функции неглобальных общих модулей вашей конфигурации. Это позволяет реализовать сложнейшие алгоритмы форматирования. Например, вы можете написать свою функцию форматирования паспортных данных в общем модуле ФизическиеЛицаСервер:


Функция СформироватьПредставлениеДокумента(Вид, КемВыдан) Экспорт
    Возврат СокрЛП(Вид) + " (выдан: " + СокрЛП(КемВыдан) + ")";
КонецФункции

А в вычисляемом поле СКД прописать следующее выражение: ФизическиеЛицаСервер.СформироватьПредставлениеДокумента(ВидДокумента, КемВыдан).

3. Агрегатная функция СКД "СоединитьСтроки"

Если вам нужно объединить строки из разных детальных записей (например, перечислить все документы, выданные физлицу за всю жизнь, через запятую в одной строке отчета), СКД предлагает мощную встроенную функцию СоединитьСтроки.

Она имеет следующий синтаксис: СоединитьСтроки(Выражение, РазделительЭлементов, РазделительКолонок). На уровне ресурсов СКД вы можете написать выражение:

СоединитьСтроки(Массив(Различные КемВыдан), ", ")

Этот код соберет все уникальные значения поля КемВыдан для группировки и соединит их через запятую, полностью игнорируя проблемы ограничений длины в СУБД. Для реализации этой задачи в отчетах можно применить готовое решение для объединения строк колонки в СКД.

Подводим итоги

Мы детально разобрали, как обойти ограничение СУБД на конкатенацию строк неограниченной длины в 1С. Подведем краткие итоги по выбору оптимального решения: при внедрении технических правок в несколько информационных баз их массовое тестирование и обслуживание упрощает программа для автоматического архивирования и тестирования баз 1С.

← На главную