Приведение поля к строке фиксированной длины в языке запросов 1С

· 9 мин

Содержание

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

Отдельного метода «ВыражатьКакСтрока» в 1С нет. Это операция языка запросов ВЫРАЗИТЬ с типом СТРОКА. Она работает на сервере СУБД, а не во встроенном языке. Поэтому её не вызывают у объекта Запрос, а пишут прямо в тексте запроса. Альтернатив две: ПОДСТРОКА(Поле, 1, N) — старый приём с тем же эффектом, и ПОДОБНО — если нужно только проверить текст, а не группировать по нему.

Зачем приводить поле к строке в запросе

У строкового реквизита в конфигураторе есть длина. Если длину поставить 0, строка становится неограниченной. Такие поля удобны для комментариев и описаний, но в запросах у них жёсткие ограничения.

Поле неограниченной длины нельзя напрямую:

  • 🔹 сравнивать через =, <>, В в условиях ГДЕ и ИМЕЮЩИЕ;
  • 🔹 указывать в СГРУППИРОВАТЬ ПО и УПОРЯДОЧИТЬ ПО;
  • 🔹 выбирать с РАЗЛИЧНЫЕ и объединять через ОБЪЕДИНИТЬ без ВСЕ (там скрыто то же «различные»);
  • 🔹 использовать в условии соединения ПО;
  • 🔹 включать в индекс временной таблицы (ИНДЕКСИРОВАТЬ ПО).

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

Вторая задача — обрезка. Если в отчёт нужно вывести только первые 50 символов наименования, приведение к СТРОКА(50) обрежет хвост прямо в СУБД. С сервера приходит меньше данных, а форма отчёта не растягивается от длинных текстов.

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

Синтаксис ВЫРАЗИТЬ и типы, к которым приводят значение

Общая форма операции:

ВЫРАЗИТЬ(<Выражение> КАК <Тип>)

Для строки тип записывают так:

ВЫРАЗИТЬ(Документ.Комментарий КАК СТРОКА(150)) КАК Комментарий

Что можно указать после КАК:

ТипЗаписьДля чего обычно
СтрокаСТРОКА(N)неограниченную строку в фиксированную, обрезка до N символов
ЧислоЧИСЛО(Длина, Точность)округление результата деления, фиксация разрядности
ДатаДАТАприведение поля составного типа к дате
БулевоБУЛЕВОприведение поля составного типа
СсылкаСправочник.Номенклатура, Документ.РеализацияТоваровУслугвыбор одного типа из составного поля, например из Регистратор

Несколько правил, которые нужно держать в голове:

  1. Ключевые слова не зависят от регистра: ВЫРАЗИТЬ, Выразить и CAST (английский синтаксис) работают одинаково.
  2. После приведения полю нужен псевдоним КАК ИмяПоля. Без него платформа назовёт колонку Поле1, и обращаться к ней в коде будет неудобно.
  3. В СГРУППИРОВАТЬ ПО повторяют то же выражение целиком, а не псевдоним.
  4. Приведение к строке работает для строковых значений. Число или дату в строку через ВЫРАЗИТЬ не перевести — запрос вернёт ошибку. Строковое представление числа получают уже в коде через Формат() или выводят поле как есть, а отчёт сам его отформатирует.

В конструкторе запроса приведение добавляют на вкладке «Таблицы и поля»: дважды щёлкните по полю в списке выбранных и допишите выражение вручную. Конструктор сохранит его и предложит задать псевдоним.

Примеры запросов с ВЫРАЗИТЬ КАК СТРОКА

Группировка документов по комментарию

Задача: посчитать, сколько реализаций за период оформлено с каждым комментарием, пустые не брать.

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

Запрос.УстановитьПараметр("НачалоПериода", НачалоМесяца(ТекущаяДатаСеанса()));
Запрос.УстановитьПараметр("КонецПериода", КонецМесяца(ТекущаяДатаСеанса()));

Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	Сообщить(Выборка.Комментарий + ": " + Формат(Выборка.ЧислоДокументов, "ЧГ=0"));
КонецЦикла;

Обратите внимание на условие КАК СТРОКА(1)) <> """". Это быстрый способ отсечь пустые комментарии: для проверки «есть текст или нет» достаточно первого символа. Внутри строкового литерала встроенного языка кавычки удваиваются, поэтому пустая строка выглядит как четыре кавычки.

Поиск по точному тексту

Если нужно найти документы с конкретным комментарием, сравнение Комментарий = &Текст не пройдёт. Рабочий вариант:

ГДЕ
	ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(100)) = &Текст

Длину берите не меньше длины искомого текста. Если искомая фраза 120 символов, а в приведении стоит 100, сравнение никогда не совпадёт: слева будет обрезанная строка.

Для поиска по вхождению приведение не нужно, ПОДОБНО работает и с неограниченными строками:

ГДЕ
	Реализация.Комментарий ПОДОБНО "%возврат%"

Соединение с таблицей значений по строковой колонке

Частая ситуация: из файла загрузили артикулы в таблицу значений и хотите сопоставить их с номенклатурой. Если колонку создали без описания типов или с типом Строка без длины, она уходит в запрос как неограниченная, и соединение ПО по ней падает.

Решение — привести колонку при помещении во временную таблицу:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	ВЫРАЗИТЬ(Загрузка.Артикул КАК СТРОКА(50)) КАК Артикул,
|	Загрузка.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Загрузка
|ИЗ
|	&ТаблицаЗагрузки КАК Загрузка
|ИНДЕКСИРОВАТЬ ПО
|	Артикул
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
|	ВТ_Загрузка.Артикул КАК Артикул,
|	ВТ_Загрузка.Количество КАК Количество,
|	Номенклатура.Ссылка КАК Номенклатура
|ИЗ
|	ВТ_Загрузка КАК ВТ_Загрузка
|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
|		ПО ВТ_Загрузка.Артикул = Номенклатура.Артикул";

Запрос.УстановитьПараметр("ТаблицаЗагрузки", ТаблицаЗагрузки);
Результат = Запрос.Выполнить().Выгрузить();

Ещё надёжнее задать тип колонки сразу при создании таблицы значений:

ТаблицаЗагрузки = Новый ТаблицаЗначений;
ТаблицаЗагрузки.Колонки.Добавить("Артикул",
	Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
ТаблицаЗагрузки.Колонки.Добавить("Количество",
	Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));

Таблица значений, переданная параметром, должна иметь типизированные колонки. Колонка вообще без типа в запросе не обработается, и ВЫРАЗИТЬ здесь уже не спасёт.

Значение составного типа

Поле Значение дополнительных сведений может хранить ссылку, число, дату или строку. Если нужны только строковые значения:

ВЫБРАТЬ
	ДопСведения.Объект КАК Объект,
	ВЫРАЗИТЬ(ДопСведения.Значение КАК СТРОКА(150)) КАК ЗначениеСтрокой
ИЗ
	РегистрСведений.ДополнительныеСведения КАК ДопСведения
ГДЕ
	ДопСведения.Свойство = &Свойство

Для нестроковых значений в колонке будет NULL. Если в отчёте нужен пустой текст, оберните выражение в ЕСТЬNULL(..., "").

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

Приведение к строке почти ничего не стоит, если оно стоит в списке полей ВЫБРАТЬ. СУБД обрезает значение при чтении строки, и всё.

Дороже становится, когда приведение попадает в условие или соединение по большой таблице:

  • 🐢 выражение над полем не даёт СУБД использовать индекс по этому полю, читается вся таблица;
  • 🐢 группировка по строке длиной 1000 символов требует больше памяти и места во временной базе СУБД, чем группировка по СТРОКА(100);
  • 🐢 соединение по приведённым строкам из двух больших таблиц почти всегда идёт полным перебором.

Практические правила:

  1. Сначала сузьте выборку по индексированным полям (дата, ссылка, организация), а условие с ВЫРАЗИТЬ ставьте вторым.
  2. Длину выбирайте по данным, а не «с запасом». Для группировки комментариев обычно хватает 150–300 символов.
  3. Если по строке нужно соединять несколько раз, приведите её один раз во временной таблице и проиндексируйте, как в примере с артикулами.
  4. Не приводите к строке поле, у которого длина и так ограничена. ВЫРАЗИТЬ(Номенклатура.Наименование КАК СТРОКА(10)) в условии ГДЕ лишает запрос индекса по наименованию. Для поиска по началу строки лучше Наименование ПОДОБНО &Начало с параметром вида "Болт%".

Отдельный случай: ВЫРАЗИТЬ для поля Регистратор

К теме строк это не относится, но тот же оператор — главный инструмент оптимизации при работе с составными ссылками. Запись Движения.Регистратор.Контрагент заставляет платформу соединить таблицу движений со всеми документами, которые могут быть регистраторами. Запись с приведением соединяет только с одним:

ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Контрагент КАК Контрагент

Как посмотреть, во что превратился запрос

Для разбора медленного запроса есть два штатных инструмента.

Замер производительности. В конфигураторе при запущенной отладке включите Отладка → Замер производительности, выполните код и посмотрите время строки Запрос.Выполнить().

Технологический журнал. Он показывает текст SQL, который реально ушёл в СУБД. Создайте файл logcfg.xml в папке conf каталога установки платформы, например C:\Program Files\1cv8\conf\:

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
	<log location="C:\v8log" history="2">
		<event>
			<eq property="name" value="DBMSSQL"/>
		</event>
		<event>
			<eq property="name" value="DBPOSTGRS"/>
		</event>
		<property name="all"/>
	</log>
</config>

Журнал с property name="all" растёт очень быстро, на рабочем сервере — гигабайты за час. Включайте его на короткое время, после разбора удалите или переименуйте logcfg.xml. Журнал подхватывается без перезапуска, сеансы пользователей не обрываются.

Как приведение к строке работает в MS SQL, PostgreSQL и файловой базе

Текст запроса 1С одинаковый для всех вариантов базы. Платформа сама переводит ВЫРАЗИТЬ в конструкцию конкретной СУБД.

Вариант базыКак хранится неограниченная строкаВо что превращается ВЫРАЗИТЬ КАК СТРОКА(N)
MS SQL Servernvarchar(max) (в старых базах встречается ntext)CAST(поле AS NVARCHAR(N))
PostgreSQL (сборка для 1С)специальный тип mvarcharприведение к mvarchar(N)
Файловая базасобственный формат платформывыполняет движок файловой базы

Отличия, которые заметны на практике:

  • 📌 Регистр букв. Сравнение строк в запросах 1С не учитывает регистр во всех вариантах. В PostgreSQL для этого и нужны типы mchar/mvarchar из сборки для 1С. Поэтому базу нельзя разворачивать на «чистом» PostgreSQL без патчей 1С.
  • 📌 Хвостовые пробелы. Строки фиксированной длины дополняются пробелами. При сравнении и группировке платформа и СУБД их не учитывают, но при выводе в код строка может прийти с пробелами. Если склеиваете результат, используйте СокрП().
  • 📌 Скорость. В файловой базе тяжёлые группировки по длинным строкам заметно медленнее, чем на клиент-серверной СУБД. Запрос, который на MS SQL идёт секунду, в файловой копии той же базы может идти минуту.

Если запрос работает в файловой копии и падает на сервере (или наоборот), причина почти никогда не в ВЫРАЗИТЬ. Чаще разница в данных: на сервере длиннее тексты или есть значения другого типа в составном поле.

Ошибки и ограничения при использовании ВЫРАЗИТЬ КАК СТРОКА

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

Ещё несколько ограничений, о которых редко пишут:

  • 🔸 Приведение работает только внутри запроса. Во встроенном языке аналога нет: там длину строки режут через Лев(Строка, N).
  • 🔸 При обрезке ничего не сообщается. Запрос не предупредит, что текст был длиннее N. Для контроля можно один раз выбрать максимальную длину: на платформе 8.3.20 и новее в запросах есть функция ДЛИНАСТРОКИ().
  • 🔸 В системе компоновки данных приведение пишут в тексте запроса набора данных. Поле Комментарий без приведения СКД сама не даст использовать в группировке отчёта.

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

Какую длину указывать в СТРОКА(N)?
Столько, сколько нужно для задачи. Для проверки «пусто или нет» хватает 1. Для группировки комментариев — 150–300. Для точного сравнения — не меньше длины искомого текста.
Чем ВЫРАЗИТЬ отличается от ПОДСТРОКА(Поле, 1, N)?
Для неограниченной строки результат одинаковый: строка длиной до N. ПОДСТРОКА умеет брать кусок из середины, ВЫРАЗИТЬ — нет, зато читается понятнее и работает с полями составного типа.
Можно ли в запросе перевести число в строку?
Через ВЫРАЗИТЬ нельзя. Выберите число как есть и отформатируйте в коде функцией Формат() или в макете отчёта.
Нужно ли приводить к строке реквизит с длиной 100?
Нет. Строка ограниченной длины уже сравнивается и группируется. Приведение понадобится, только если нужно её обрезать.
Работает ли ВЫРАЗИТЬ в английском синтаксисе запроса?
Да, это CAST(Поле AS STRING(N)). Платформа понимает оба варианта.