Как найти и вырезать фрагмент строки в запросе 1С 8.3
Содержание
Чтобы отобрать записи, где поле содержит подстроку, в запросе 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, 2)вернёт «БП» из «БП-000123». Удобно, если префиксом размечена организация или узел обмена. - Регион по ИНН.
ПОДСТРОКА(Контрагенты.ИНН, 1, 2)— первые две цифры, код региона постановки на учёт. - Работа со строкой неограниченной длины. Поля вроде «Комментарий» часто имеют неограниченную длину. Сравнить их через
=, сгруппировать или упорядочить нельзя — запрос выдаст ошибку. Обходной приём:ПОДСТРОКА(Документ.Комментарий, 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 миллионов строк он способен повесить отчёт на минуты. Что реально помогает:
- Сначала сузьте выборку дешёвыми условиями. Период, организация, вид документа — эти поля индексированы. Подстроку проверяйте уже на оставшихся строках.
- Ищите по началу, если задача позволяет. «Артикул начинается с ABC» (
"ABC%") почти всегда быстрее, чем «содержит ABC». - Не оборачивайте поле в функцию в
ГДЕбез нужды. УсловиеПОДСТРОКА(Поле, 1, 3) = "ABC"по смыслу равноПоле ПОДОБНО "ABC%", но индекс по полю СУБД на функции не применит. - Проверьте свойство «Индексировать» у реквизита. Код и наименование справочников индексируются платформой сами. Для обычного реквизита индекс включают в его свойствах в конфигураторе — и после этого нужно обновление конфигурации базы данных.
- Не ищите по представлению ссылки. Отбор по
Поле.Наименованиетянет соединение с таблицей справочника. Если ищете по контрагенту, отберите контрагентов отдельным запросом, а потом используйтеВ (&Контрагенты). - Для поиска «по всему» — полнотекстовый поиск, а не цепочка
ИЛИ ... ПОДОБНОпо десяти полям.