Строка в запросе 1С: ПОДОБНО, ПОДСТРОКА и строки неограниченной длины
Поиск по части названия, отбор по началу ИНН, сборка описания из нескольких полей — со строками в запросах приходится работать постоянно, и у них есть свои ограничения. Статья объясняет, как записывать строковые литералы и параметры, как устроен ПОДОБНО с шаблонами и СПЕЦСИМВОЛ, как использовать ПОДСТРОКА и что делать со строками неограниченной длины. В конце — готовая функция поиска по нескольким словам и список типичных ошибок.
Задача: искать и преобразовывать текст прямо в запросе
Строки в запросах 1С нужны постоянно: поиск номенклатуры по части названия, отбор контрагентов по началу ИНН, сборка описания из нескольких полей, сравнение комментария с образцом. При этом у строк в языке запросов есть жёсткие ограничения — особенно у полей неограниченной длины, — и большинство «непонятных» ошибок связано именно с ними. Разберём всё по порядку: от литералов и параметров до шаблонов ПОДОБНО, функции ПОДСТРОКА и приведения типов через ВЫРАЗИТЬ. Общие приёмы работы с объектом Запрос описаны в статье про запросы в 1С.
Строковый литерал и параметр
В тексте запроса строковая константа записывается в двойных кавычках. Но сам текст запроса в коде — тоже строка 1С, поэтому внутренние кавычки приходится удваивать. Ошибка в количестве кавычек — самая частая причина, по которой запрос «не парсится» уже при создании.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.Ссылка КАК Контрагент
|ИЗ
| Справочник.Контрагенты КАК Контрагенты
|ГДЕ
| Контрагенты.Наименование = &Наименование
| ИЛИ Контрагенты.ИНН = ""7701234567""";
Запрос.УстановитьПараметр("Наименование", Наименование);
Если значение приходит из формы, файла или внешней системы, всегда передавайте его параметром, а не склеивайте с текстом запроса. Параметр не ломается от кавычек и служебных символов внутри значения, а платформа может переиспользовать план выполнения. Литерал оставляйте для неизменяемых констант вроде кодов и статусов.
ПОДОБНО: сравнение с шаблоном
Оператор ПОДОБНО сравнивает строку с шаблоном. В шаблоне работают четыре служебных конструкции: % — любая последовательность символов (в том числе пустая), _ — ровно один любой символ, [...] — один символ из перечисленных или из диапазона, [^...] — один символ, которого нет в перечислении.
ГДЕ
Номенклатура.Наименование ПОДОБНО "Кабель%" // начинается с «Кабель»
ИЛИ Номенклатура.Наименование ПОДОБНО "%ВВГ%" // содержит «ВВГ»
ИЛИ Номенклатура.Артикул ПОДОБНО "КБ-___" // «КБ-» и ещё три любых символа
ИЛИ Номенклатура.Артикул ПОДОБНО "[0-9][0-9]%" // начинается с двух цифр
ИЛИ Номенклатура.Артикул ПОДОБНО "[^0-9]%" // не начинается с цифры
Обычное сравнение = шаблонов не понимает: условие Наименование = "Кабель%" ищет строку, буквально заканчивающуюся знаком процента. Шаблон можно передать и параметром, тогда процент добавляют в коде: "%" + СтрокаПоиска + "%".
Учтите производительность. Шаблон вида "Кабель%" (поиск по началу) СУБД может выполнять по индексу, а "%кабель%" заставляет просматривать всю таблицу. На небольших справочниках это незаметно, на миллионах строк — заметно, поэтому для массового поиска используйте полнотекстовый поиск или ограничивайте выборку другими условиями.
СПЕЦСИМВОЛ: поиск знаков % и _ как обычного текста
Если искомый текст сам содержит процент, подчёркивание или квадратную скобку, их нужно «экранировать». Для этого после шаблона указывается ключевое слово СПЕЦСИМВОЛ и символ-экранировщик, а в шаблоне этот символ ставится перед служебным знаком.
ВЫБРАТЬ
Скидки.Ссылка КАК Ссылка
ИЗ
Справочник.ВидыСкидок КАК Скидки
ГДЕ
Скидки.Наименование ПОДОБНО "%50\%%" СПЕЦСИМВОЛ "\"
Этот запрос найдёт все названия, где встречается «50%». Когда строка поиска вводится пользователем, экранирование лучше вынести в отдельную функцию, иначе символ подчёркивания в артикуле превратится в «любой символ», а скобка вызовет ошибку шаблона.
Функция ЭкранироватьШаблон(Знач Строка) Экспорт
Результат = СтрЗаменить(Строка, "\", "\\");
Результат = СтрЗаменить(Результат, "%", "\%");
Результат = СтрЗаменить(Результат, "_", "\_");
Результат = СтрЗаменить(Результат, "[", "\[");
Результат = СтрЗаменить(Результат, "]", "\]");
Возврат Результат;
КонецФункции
Поиск по нескольким словам
Типовая задача — строка поиска «кабель ввг 3х2.5», где слова могут стоять в любом порядке. Решение: разбить строку на слова и на каждое слово добавить своё условие ПОДОБНО с отдельным параметром.
&НаСервере
Функция НайтиНоменклатуру(СтрокаПоиска)
Слова = СтрРазделить(СтрокаПоиска, " ", Ложь);
Если Слова.Количество() = 0 Тогда
Возврат Новый Массив;
КонецЕсли;
Запрос = Новый Запрос;
Условия = Новый Массив;
Для Индекс = 0 По Слова.ВГраница() Цикл
ИмяПараметра = "Слово" + XMLСтрока(Индекс);
Условия.Добавить("Номенклатура.Наименование ПОДОБНО &" + ИмяПараметра + " СПЕЦСИМВОЛ ""\""");
Запрос.УстановитьПараметр(ИмяПараметра, "%" + ЭкранироватьШаблон(Слова[Индекс]) + "%");
КонецЦикла;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 50
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| " + СтрСоединить(Условия, " И ");
Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
КонецФункции
Здесь текст запроса собирается динамически, но значения по-прежнему идут параметрами — подстановки пользовательского текста в запрос нет.
ПОДСТРОКА: часть строки в запросе
Функция ПОДСТРОКА(Строка, НачальнаяПозиция, Длина) вырезает фрагмент. Позиции считаются с единицы. Типичные применения — группировка по первой букве или отбор по региональной части ИНН.
ВЫБРАТЬ
ПОДСТРОКА(Контрагенты.Наименование, 1, 1) КАК Буква,
КОЛИЧЕСТВО(*) КАК Количество
ИЗ
Справочник.Контрагенты КАК Контрагенты
ГДЕ
ПОДСТРОКА(Контрагенты.ИНН, 1, 2) = "77"
СГРУППИРОВАТЬ ПО
ПОДСТРОКА(Контрагенты.Наименование, 1, 1)
УПОРЯДОЧИТЬ ПО
Буква
Условие по ПОДСТРОКА применяется к каждой строке таблицы и не использует индекс по полю. Для поиска по началу строки проще и быстрее ПОДОБНО "77%".
Строки неограниченной длины: ВЫРАЗИТЬ и ПОДСТРОКА
Реквизиты со строкой неограниченной длины — чаще всего «Комментарий» у документов и произвольные текстовые поля — хранятся в СУБД особым образом, и к ним применяются ограничения. Такие поля нельзя напрямую сравнивать операторами = и <>, группировать, использовать в упорядочивании, в РАЗЛИЧНЫЕ и в условиях соединения. Платформа сообщит об этом ошибкой при выполнении запроса. Поиск по подстроке при этом работает через ПОДОБНО.
Чтобы сравнить такое поле или сгруппировать по нему, его приводят к строке ограниченной длины. Допустимая длина ограничена 1024 символами.
ВЫБРАТЬ
Реализация.Ссылка КАК Ссылка
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
ГДЕ
ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(200)) = &Комментарий
// Альтернатива — вырезать начало строки
// ПОДСТРОКА(Реализация.Комментарий, 1, 200) = &Комментарий
Обратите внимание: приведение обрезает значение до указанной длины. Если в комментарии больше 200 символов, сравнение пройдёт только по первым двухстам. Для группировки приведённое выражение указывают и в списке полей, и в СГРУППИРОВАТЬ ПО.
ВЫБРАТЬ
ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(100)) КАК Комментарий,
КОЛИЧЕСТВО(*) КАК Количество
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
СГРУППИРОВАТЬ ПО
ВЫРАЗИТЬ(Реализация.Комментарий КАК СТРОКА(100))
Конкатенация и число в строку
Строки складываются знаком +. Складывать можно только строки: число и дату сначала приводят к строке через ВЫРАЗИТЬ(... КАК СТРОКА(N)), ссылку — через ПРЕДСТАВЛЕНИЕ(). Длина должна быть достаточной для значения, поэтому для чисел берут запас.
ВЫБРАТЬ
Реализация.Номер + " на сумму "
+ ВЫРАЗИТЬ(Реализация.СуммаДокумента КАК СТРОКА(20)) + " руб." КАК Описание,
ПРЕДСТАВЛЕНИЕ(Реализация.Контрагент) КАК КонтрагентПредставление
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
Важная особенность: если хотя бы одно слагаемое равно NULL (например, поле присоединено левым соединением и записи не нашлось), вся сумма станет NULL. Оборачивайте такие поля в ЕСТЬNULL(Поле, ""). Форматирование чисел с разделителями разрядов в запросе не делается — для этого есть функция Формат() встроенного языка или оформление в СКД.
Новые строковые функции языка запросов
Долгое время в языке запросов было всего две строковые функции — ПОДСТРОКА и приведение через ВЫРАЗИТЬ; всё остальное делали в коде или обходили шаблонами. В новых версиях платформы 8.3 язык запросов пополнился знакомыми функциями встроенного языка: длина строки (СТРДЛИНА), удаление пробелов (СОКРЛП), смена регистра (ВРЕГ, НРЕГ), поиск и замена подстроки (СТРНАЙТИ, СТРЗАМЕНИТЬ), выбор левой и правой части (ЛЕВ, ПРАВ). Точный набор и сигнатуры зависят от релиза, поэтому проверьте их в синтакс-помощнике своей версии. Если в вашей платформе они есть, запрос становится короче:
ВЫБРАТЬ
Номенклатура.Ссылка КАК Ссылка,
СТРДЛИНА(Номенклатура.Наименование) КАК Длина,
СОКРЛП(Номенклатура.Артикул) КАК Артикул
ИЗ
Справочник.Номенклатура КАК Номенклатура
ГДЕ
СТРНАЙТИ(ВРЕГ(Номенклатура.Наименование), ВРЕГ(&Слово)) > 0
В конфигурациях на более старых релизах те же задачи решают через ПОДСТРОКА, ПОДОБНО и постобработку результата в коде.
Регистр букв при поиске
В стандартных настройках информационной базы сравнение строк в запросе не различает регистр: "кабель", "Кабель" и "КАБЕЛЬ" считаются равными, а ПОДОБНО находит слово в любом регистре. Поэтому приводить обе стороны к одному регистру обычно не требуется. Если поведение критично (например, в базе нестандартная настройка СУБД), нормализуйте и поле, и значение через ВРЕГ, а параметр для надёжности приведите в верхний регистр функцией ВРег() встроенного языка.
Типичные ошибки со строками
- Не удвоены кавычки внутри текста запроса — запрос не разбирается или обрезается раньше времени.
- Сравнение или группировка по полю неограниченной длины без
ВЫРАЗИТЬ— ошибка при выполнении. - В
ПОДОБНОпередан параметр без знаков%— условие работает как точное равенство. - Пользовательский ввод с символами
%,_,[не экранирован — поиск находит лишнее или падает. - Слагаемое со значением
NULL«обнуляет» всю конкатенацию. - Слишком короткий
СТРОКА(N)молча обрезает данные. - Расчёт на то, что ПОДСТРОКА использует индекс — для поиска по началу применяйте
ПОДОБНО.
Шаблоны ПОДОБНО: шпаргалка
| Шаблон | Что находит |
|---|---|
"кабель%" | Строки, начинающиеся с «кабель» |
"%кабель" | Строки, заканчивающиеся на «кабель» |
"%кабель%" | Строки, содержащие «кабель» |
"К_бель" | «Кабель», «Кобель» — любой один символ на месте подчёркивания |
"[а-я]%" | Начинается со строчной русской буквы |
"[^0-9]%" | Не начинается с цифры |
"%\%" СПЕЦСИМВОЛ "\" | Заканчивается знаком процента |
Актуальность для новых версий
Оператор ПОДОБНО, функции ПОДСТРОКА и ВЫРАЗИТЬ работают одинаково во всех версиях платформы 8.3. В новых релизах 8.3 язык запросов получил дополнительные строковые функции (СТРДЛИНА, СТРНАЙТИ, СТРЗАМЕНИТЬ, ВРЕГ, НРЕГ, СОКРЛП, ЛЕВ, ПРАВ) — перед использованием проверьте их наличие в синтакс-помощнике своего релиза. Запросы, написанные только на ПОДОБНО, ПОДСТРОКА и ВЫРАЗИТЬ, совместимы с любой версией 8.3.
Частые вопросы
Как сравнить поле неограниченной длины в запросе 1С?
Приведите его к строке ограниченной длины: ВЫРАЗИТЬ(Поле КАК СТРОКА(200)) = &Значение или ПОДСТРОКА(Поле, 1, 200) = &Значение. Напрямую такие поля нельзя сравнивать, группировать и упорядочивать.
Как найти в запросе 1С строки, содержащие знак процента или подчёркивание?
Добавьте после шаблона ключевое слово СПЕЦСИМВОЛ и символ-экранировщик: Поле ПОДОБНО "%50\%%" СПЕЦСИМВОЛ "\". Экранированный знак воспринимается как обычный текст.
Зачем в ПОДОБНО нужны знаки процента?
Знак % заменяет любую последовательность символов. Без него ПОДОБНО работает как точное сравнение: "Кабель" найдёт только строку «Кабель», а "%кабель%" — любую строку, где есть это слово.
Почему конкатенация в запросе возвращает NULL?
Если одно из слагаемых равно NULL, например после левого соединения, вся сумма строк тоже становится NULL. Оберните такие поля в ЕСТЬNULL(Поле, "").