Часто перед разработчиком встает задача организовать поиск или фильтрацию текстовых данных по определенному образцу. Типичный пример — задача о системном администраторе Мише, которому нужно отфильтровать логины пользователей, используя привычные многим спецсимволы * (любое количество символов) и ? (один произвольный символ). В этой статье мы подробно разберем, как реализовать такой механизм в среде «1С:Предприятие 8» с использованием языка запросов и встроенного языка.
Для решения подобных задач в языке запросов 1С существует специальный оператор ПОДОБНО (в английском варианте LIKE). Однако его синтаксис отличается от того, что привык вводить пользователь. Рассмотрим соответствие символов:
*).?).Таким образом, наша первая задача при получении шаблона от пользователя — преобразовать его в формат, понятный SQL-серверу и платформе 1С.
Предположим, пользователь ввел шаблон в поле ввода на форме отчета. Перед тем как передать эту строку в запрос, мы должны выполнить замену символов. Проанализируем, как это сделать правильно:
// Получаем шаблон из реквизита формы
ШаблонДляЗапроса = Объект.ШаблонПоиска;
// Выполняем последовательную замену
ШаблонДляЗапроса = СтрЗаменить(ШаблонДляЗапроса, "*", "%");
ШаблонДляЗапроса = СтрЗаменить(ШаблонДляЗапроса, "?", "_");
Важный нюанс: согласно условиям задачи, логины состоят только из латинских букв и цифр. Если бы в логинах могли встречаться сами символы % или _, нам пришлось бы использовать конструкцию СПЕЦСИМВОЛ (ESCAPE) для их экранирования, чтобы запрос не воспринял их как команды фильтрации.
Теперь рассмотрим, как использовать подготовленную строку в запросе. Нам необходимо выбрать данные из табличной части документа и сравнить реквизит Логин с нашим шаблоном. Выясним причину, по которой важно правильно типизировать данные.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПользователиПользователи.Номер КАК НомерСтроки,
| ПользователиПользователи.ФИО КАК ФИО,
| ПользователиПользователи.Логин КАК Логин
|ИЗ
| Документ.Пользователи.Пользователи КАК ПользователиПользователи
|ГДЕ
| ПользователиПользователи.Ссылка = &СсылкаНаДокумент
| И ПользователиПользователи.Логин ПОДОБНО &Шаблон
|УПОРЯДОЧИТЬ ПО
| НомерСтроки";
Запрос.УстановитьПараметр("СсылкаНаДокумент", Объект.ДокументСсылка);
Запрос.УстановитьПараметр("Шаблон", ШаблонДляЗапроса);
РезультатЗапроса = Запрос.Выполнить();
Проанализируем ситуацию, упомянутую в обсуждении на форуме: как будут сортироваться номера строк? Если поле Номер в табличной части имеет строковый тип, то при обычной сортировке мы получим порядок: 1, 10, 2, 20. Это называется лексикографической сортировкой.
Чтобы отчет вывел данные в правильном математическом порядке, в запросе следует использовать приведение типов или заранее убедиться, что реквизит имеет числовой тип. Если это строка, используем ВЫРАЗИТЬ:
// В тексте запроса заменим сортировку
|УПОРЯДОЧИТЬ ПО
| ВЫРАЗИТЬ(ПользователиПользователи.Номер КАК ЧИСЛО)
Однако помните, что ВЫРАЗИТЬ для строк работает не во всех СУБД одинаково. Оптимальный вариант — хранить номер в числовом реквизите.
После выполнения запроса нам нужно вывести данные в отчет (например, в табличный документ) и подсчитать количество найденных строк. Рассмотрим пример реализации вывода:
Выборка = РезультатЗапроса.Выбрать();
ТабДок = Новый ТабличныйДокумент;
// Здесь должен быть код вывода шапки таблицы...
КоличествоСтрок = 0;
Пока Выборка.Следующий() Цикл
КоличествоСтрок = КоличествоСтрок + 1;
// Код вывода строки: Выборка.НомерСтроки, Выборка.ФИО, Выборка.Логин
КонецЦикла;
// Вывод итога
Сообщить("Итого строк = " + КоличествоСтрок);
Посмотрим на настройки поиска. Оператор ПОДОБНО в 1С обычно ведет себя так же, как настроена база данных SQL (Case Insensitivity). Если ваша база данных чувствительна к регистру (редкий случай для 1С), а логины в системе хранятся в разном регистре, рекомендуется использовать функцию ВРЕГ (ВЕРХНИЙ РЕГИСТР) как для поля в запросе, так и для шаблона:
|ГДЕ
| ВРЕГ(ПользователиПользователи.Логин) ПОДОБНО ВРЕГ(&Шаблон)
Если задача усложнится (например, потребуется найти логины, где цифр не более трех), возможностей ПОДОБНО может не хватить. В таких случаях программисты 1С обращаются к объекту VBScript.RegExp через COM-соединение. Рассмотрим, как это выглядит для нашей задачи:
RegExp = Новый COMОбъект("VBScript.RegExp");* превращается в .*, а ? в ..^ и $.Пример преобразования шаблона A???B в регулярное выражение:
RegExp.Pattern = "^A...B$";
RegExp.IgnoreCase = Истина;
Если RegExp.Test(ПроверяемыйЛогин) Тогда
// Логин подходит
КонецЕсли;
Однако для большинства олимпиадных и прикладных задач в 1С использование оператора ПОДОБНО является наиболее эффективным и быстрым способом, так как фильтрация происходит на стороне сервера базы данных.