Как найти и вырезать фрагмент строки в запросе 1С 8.3

· 9 мин

Содержание

Чтобы отобрать записи, где поле содержит подстроку, в запросе 1С 8 пишут ГДЕ Поле ПОДОБНО "%текст%". Чтобы вырезать часть строки — ПОДСТРОКА(Поле, Начало, Длина). А для позиции фрагмента в новых платформах (8.3.20 и выше) есть СТРНАЙТИ.

Частое заблуждение — что в языке запросов работают те же функции, что во встроенном языке: Найти(), СтрНайти() с четырьмя параметрами, ПоискСтроки. Не работают. Язык запросов транслируется в SQL, и набор строковых функций в нём свой, заметно беднее. Разберём, что в нём есть, как этим пользоваться и где поиск по подстроке начинает тормозить базу.

Какие средства поиска подстроки есть в языке запросов

Сначала — карта инструментов. Всё, что ниже, работает и в файловой, и в клиент-серверной базе.

СредствоЧто делаетС какой версии платформы
ПОДОБНОСравнивает строку с шаблоном (%, _, [...])Давно, во всех 8.x
ПОДСТРОКА(Стр, Нач, Длина)Вырезает фрагмент строкиВо всех 8.x
СТРНАЙТИ(Стр, Подстрока)Возвращает позицию фрагмента, 0 — если нет8.3.20
ЛЕВ, ПРАВ, СТРДЛИНАНачало, конец, длина строки8.3.20
СОКРЛ, СОКРП, СОКРЛПУбирают пробелы по краям8.3.20
ВРЕГ, НРЕГ, СТРЗАМЕНИТЬРегистр и замена фрагмента8.3.20

Если функция из нижней половины таблицы даёт синтаксическую ошибку, проверьте две вещи: версию платформы и режим совместимости конфигурации. Пока режим совместимости ниже 8.3.20, новые функции в запросе недоступны.

Отдельной функции «ПоискСтроки» в запросе нет. Её роль выполняют либо ПОДОБНО, либо СТРНАЙТИ(...) > 0.

Оператор ПОДОБНО: поиск подстроки по шаблону

ПОДОБНО — русское имя SQL-оператора LIKE. Можно писать и так, и так: Поле LIKE "%текст%" тоже скомпилируется.

Спецсимволы шаблона

  • 🔹 % — любое количество любых символов, в том числе ноль;
  • 🔹 _ — ровно один любой символ;
  • 🔹 [абв] — один символ из перечня;
  • 🔹 [0-9] — один символ из диапазона;
  • 🔹 [^0-9] — один символ, которого нет в диапазоне.

Примеры шаблонов и что они найдут:

ШаблонНайдётНе найдёт
"%болт%"«Болт М8», «Анкерный болт»«Шуруп М8»
"Болт%"«Болт М8»«Анкерный болт»
"%-01"«Кронштейн-01»«Кронштейн-011»
"А-___"«А-123», «А-XYZ»«А-12», «А-1234»
"[0-9]%"всё, что начинается с цифры«А-123»

Регистр букв при сравнении в запросе не учитывается: «болт» и «Болт» совпадут.

Шаблон из параметра

Писать шаблон прямо в текст запроса удобно только в консоли. В коде шаблон передают параметром:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Номенклатура.Ссылка,
|	Номенклатура.Наименование
|ИЗ
|	Справочник.Номенклатура КАК Номенклатура
|ГДЕ
|	Номенклатура.Наименование ПОДОБНО &Шаблон";

Запрос.УстановитьПараметр("Шаблон", "%" + СтрокаПоиска + "%");
Выборка = Запрос.Выполнить().Выбрать();

Знаки % можно приклеить и в самом запросе: ПОДОБНО "%" + &СтрокаПоиска + "%". Конкатенация через + в языке запросов разрешена. Но вариант с готовым параметром проще отлаживать: шаблон видно целиком.

Экранирование: когда пользователь вводит «%» или «_»

Вот где ломается большинство самописных отборов. Пользователь ищет артикул AB_12, а запрос возвращает и «AB-12», и «ABX12». Подчёркивание сработало как шаблон «любой символ».

Лечится ключевым словом СПЕЦСИМВОЛ (в английском синтаксисе ESCAPE). Оно назначает символ, после которого спецзнак считается обычной буквой. Обычно берут тильду:

Функция ЭкранироватьДляПодобно(Знач Текст)
	Текст = СтрЗаменить(Текст, "~", "~~");
	Текст = СтрЗаменить(Текст, "%", "~%");
	Текст = СтрЗаменить(Текст, "_", "~_");
	Текст = СтрЗаменить(Текст, "[", "~[");
	Текст = СтрЗаменить(Текст, "]", "~]");
	Текст = СтрЗаменить(Текст, "^", "~^");
	Возврат Текст;
КонецФункции

Тильду заменяем первой — иначе удвоим те тильды, которые сами же только что поставили. Дальше в запросе:

"... ГДЕ Номенклатура.Артикул ПОДОБНО &Шаблон СПЕЦСИМВОЛ ""~""";

Запрос.УстановитьПараметр("Шаблон", "%" + ЭкранироватьДляПодобно(СтрокаПоиска) + "%");

Без экранирования строка поиска с [ может дать пустой результат или неожиданную выборку: квадратная скобка открывает перечень символов. В отборах, куда текст вводит пользователь, экранируйте всегда, даже если «у нас таких артикулов нет».

ПОДСТРОКА, СТРНАЙТИ и другие строковые функции запроса

ПОДСТРОКА: вырезать фрагмент

Синтаксис: ПОДСТРОКА(Строка, НачальнаяПозиция, Длина). Позиции считаются с 1. Все три параметра обязательны: в отличие от Сред() во встроенном языке, «до конца строки» без длины не получится.

Типичные задачи:

  1. Префикс номера документа. ПОДСТРОКА(Документ.Номер, 1, 2) вернёт «БП» из «БП-000123». Удобно, если префиксом размечена организация или узел обмена.
  2. Регион по ИНН. ПОДСТРОКА(Контрагенты.ИНН, 1, 2) — первые две цифры, код региона постановки на учёт.
  3. Работа со строкой неограниченной длины. Поля вроде «Комментарий» часто имеют неограниченную длину. Сравнить их через =, сгруппировать или упорядочить нельзя — запрос выдаст ошибку. Обходной приём: ПОДСТРОКА(Документ.Комментарий, 1, 300). Платформа получает строку фиксированной длины, и с ней уже можно делать всё.
ВЫБРАТЬ
	ПОДСТРОКА(Реализация.Комментарий, 1, 300) КАК Комментарий,
	КОЛИЧЕСТВО(*) КАК Документов
ИЗ
	Документ.РеализацияТоваровУслуг КАК Реализация
СГРУППИРОВАТЬ ПО
	ПОДСТРОКА(Реализация.Комментарий, 1, 300)

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

СТРНАЙТИ: позиция подстроки

СТРНАЙТИ(Строка, ПодстрокаПоиска) возвращает номер символа, с которого начинается первое вхождение. Если вхождения нет — 0.

Отбор «содержит» через неё выглядит так:

ГДЕ СТРНАЙТИ(Контрагенты.Наименование, &Фрагмент) > 0

Плюс по сравнению с ПОДОБНО: не нужно экранировать %, _ и скобки, фрагмент ищется буквально. Минус: работает только на платформе 8.3.20 и выше.

Главная сила СТРНАЙТИ — в связке с ПОДСТРОКА. Например, вытащить всё, что стоит до первого дефиса в артикуле:

ВЫБРАТЬ
	Номенклатура.Артикул,
	ВЫБОР
		КОГДА СТРНАЙТИ(Номенклатура.Артикул, "-") > 0
			ТОГДА ЛЕВ(Номенклатура.Артикул, СТРНАЙТИ(Номенклатура.Артикул, "-") - 1)
		ИНАЧЕ Номенклатура.Артикул
	КОНЕЦ КАК Серия
ИЗ
	Справочник.Номенклатура КАК Номенклатура

Для «ABC-0045» получим «ABC»: дефис на 4-й позиции, берём 3 символа слева. Проверка > 0 нужна, иначе для артикула без дефиса ЛЕВ получил бы длину −1.

Остальные функции 8.3.20

  • ✂️ СОКРЛП(Поле) — когда в данных из обмена болтаются пробелы и = не срабатывает;
  • 🔠 ВРЕГ, НРЕГ — для вывода; для сравнения не нужны, регистр и так не учитывается;
  • 🔁 СТРЗАМЕНИТЬ(Поле, " ", "") — сравнить телефоны или ИНН, записанные с пробелами;
  • 📏 СТРДЛИНА(Поле) — найти «кривые» ИНН: ГДЕ СТРДЛИНА(Контрагенты.ИНН) НЕ В (10, 12).

Как ПОДОБНО ведёт себя в файловой базе, MS SQL и PostgreSQL

Текст запроса один, а выполняет его разная СУБД. 1С транслирует ПОДОБНО в LIKE нужного диалекта и берёт на себя основные различия, поэтому результат одинаковый. Различается скорость и мелочи вокруг неё.

БазаЧто учесть
ФайловаяНет оптимизатора уровня SQL-сервера. Поиск с ведущим % по большому справочнику перебирает его целиком прямо на клиентском компьютере или сервере, где лежит файл
MS SQL ServerШаблон без ведущего % («начинается с») может использовать индекс по полю. Шаблон %текст% — почти всегда полный просмотр
PostgreSQLДля строк 1С использует собственный тип с нечувствительным к регистру сравнением. Поиск «начинается с» тоже может идти по индексу, но проверяйте план запроса на своих данных

Правило для всех трёх одно: чем раньше в шаблоне стоит %, тем меньше СУБД может помочь индексом.

Строки неограниченной длины на стороне СУБД хранятся в типе, по которому индексы не строятся. Поиск подстроки по такому полю всегда идёт перебором — учитывайте это до того, как отдать отбор по «Комментарию» пользователям.

Полнотекстовый поиск: когда ПОДОБНО уже не тянет

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

Чем он отличается от ПОДОБНО:

  • 📚 ищет по заранее построенному индексу, а не перебором таблиц;
  • 🔤 учитывает морфологию: по слову «договор» найдёт «договора», «договору»;
  • 🧩 ищет по нескольким словам сразу, в любом порядке;
  • ⏳ видит только то, что уже попало в индекс — свежие изменения появятся после его обновления.

Включается он в конфигураторе свойством объекта метаданных «Полнотекстовый поиск» (у объекта и у его реквизитов) и режимом в информационной базе. В типовых конфигурациях на БСП настройка вынесена в Администрирование → Общие настройки: там включают поиск и управляют обновлением индекса по расписанию.

Связка с запросом выглядит так: сначала полнотекстовый поиск находит ссылки, потом обычный запрос дочитывает по ним нужные поля.

СписокПоиска = ПолнотекстовыйПоиск.СоздатьСписок("поставка арматуры", 100);
СписокПоиска.ПерваяЧасть();

Ссылки = Новый Массив;
Для Каждого Элемент Из СписокПоиска Цикл
	Ссылки.Добавить(Элемент.Значение);
КонецЦикла;

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

В Ссылки попадут объекты разных типов, а запрос оставит только договоры. Если нужны все типы — разбирайте Элемент.Метаданные прямо в цикле, без запроса.

Полное перестроение индекса на большой базе нагружает сервер и идёт долго. Запускайте его вне рабочего времени. Обычное регламентное обновление, которое добавляет изменённые объекты, работает в фоне и пользователям не мешает.

Скорость поиска подстроки на больших таблицах

Отбор ПОДОБНО "%...%" по справочнику на 5 000 элементов незаметен. По регистру на 20 миллионов строк он способен повесить отчёт на минуты. Что реально помогает:

  1. Сначала сузьте выборку дешёвыми условиями. Период, организация, вид документа — эти поля индексированы. Подстроку проверяйте уже на оставшихся строках.
  2. Ищите по началу, если задача позволяет. «Артикул начинается с ABC» ("ABC%") почти всегда быстрее, чем «содержит ABC».
  3. Не оборачивайте поле в функцию в ГДЕ без нужды. Условие ПОДСТРОКА(Поле, 1, 3) = "ABC" по смыслу равно Поле ПОДОБНО "ABC%", но индекс по полю СУБД на функции не применит.
  4. Проверьте свойство «Индексировать» у реквизита. Код и наименование справочников индексируются платформой сами. Для обычного реквизита индекс включают в его свойствах в конфигураторе — и после этого нужно обновление конфигурации базы данных.
  5. Не ищите по представлению ссылки. Отбор по Поле.Наименование тянет соединение с таблицей справочника. Если ищете по контрагенту, отберите контрагентов отдельным запросом, а потом используйте В (&Контрагенты).
  6. Для поиска «по всему» — полнотекстовый поиск, а не цепочка ИЛИ ... ПОДОБНО по десяти полям.
Проверьте себя
Какой шаблон найдёт «Кабель 3х2,5», но не найдёт «Удлинитель с кабелем»?
Пользователь ищет артикул AB_12. Что нужно, чтобы не найти AB-12?
Почему ПОДСТРОКА(Комментарий, 1, 300) помогает в группировке?

Ошибки, на которых спотыкаются чаще всего

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

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

Можно ли в запросе искать подстроку без учёта регистра?
Да, по умолчанию так и есть. Сравнение строк в запросе 1С регистр не учитывает, ВРЕГ и НРЕГ для этого не нужны.
Можно ли в шаблоне ПОДОБНО использовать поле другой таблицы?
Надёжнее передавать шаблон параметром или строковым литералом. Если нужно сравнить два поля между собой, на платформе 8.3.20 и выше удобнее условие СТРНАЙТИ(Поле1, Поле2) > 0.
Как найти записи, где поле НЕ содержит фрагмент?
Через НЕ: ГДЕ НЕ Поле ПОДОБНО “%текст%”. Аналог на новых платформах — СТРНАЙТИ(Поле, “текст”) = 0.
Работает ли ПОДОБНО в СКД и в динамических списках?
Да. В СКД это вид сравнения «Подобно» в отборе, в тексте запроса набора данных — обычный оператор. В динамическом списке оператор можно писать прямо в произвольном запросе списка.
Чем ПОДОБНО отличается от поиска в форме списка?
Строка поиска в форме списка формирует отбор сама и может использовать полнотекстовый поиск, если он включён. ПОДОБНО вы контролируете полностью: какие поля, какой шаблон, какой порядок условий.