При разработке отчетов и обработок в 1С нередко возникает задача сопоставления строковых данных: например, проверить, присутствуют ли все символы из одной строки (например, "15") внутри другой строки (например, "12345"). Разберем, как правильно реализовать такую проверку в запросе и во встроенном языке, а также проанализируем ограничения оператора ПОДОБНО.
Частая ошибка при решении этой задачи — попытка использовать квадратные скобки в шаблоне: ПОДОБНО "%[15]%". Важно помнить о правилах языка запросов 1С:
[...] означает вхождение хотя бы одного символа из указанного перечня, а не всех символов одновременно. Шаблон "%[15]%" вернет истину и для строки "100", и для "500", даже если второго символа там нет."%..."), СУБД не может использовать индексы, что приводит к полному сканированию таблицы (Table Scan). На больших объемах данных это существенно снижает производительность.%, _, [, ], ^), шаблон вызовет ошибку или сработает неверно. В таких случаях обязательно экранирование через конструкцию СПЕЦСИМВОЛ.Рассмотрим наиболее гибкий и быстрый по производительности способ, когда исходная строка-шаблон известна до выполнения запроса. Мы можем разбить строку на отдельные символы во встроенном языке и сформировать цепочку условий через оператор И.
Разберем по шагам реализацию этого подхода:
// Исходные данные для проверки
СтрокаСимволов = "15";
МассивУсловий = Новый Массив;
ПараметрыЗапроса = Новый Структура;
Для Номер = 1 По СтрДлина(СтрокаСимволов) Цикл
ТекущийСимвол = Сред(СтрокаСимволов, Номер, 1);
// Имя параметра для каждого символа
ИмяПараметра = "Символ_" + Формат(Номер, "ЧГ=0");
МассивУсловий.Добавить("Таблица.СтрокаПоиска ПОДОБНО &" + ИмяПараметра);
ПараметрыЗапроса.Вставить(ИмяПараметра, "%" + ТекущийСимвол + "%");
КонецЦикла;
// Собираем итоговое условие ГДЕ
Если МассивУсловий.Количество() > 0 Тогда
ТекстУсловия = "ГДЕ " + СтрСоединить(МассивУсловий, " И ");
Иначе
ТекстУсловия = "";
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Таблица.Ссылка КАК Ссылка,
| Таблица.СтрокаПоиска КАК СтрокаПоиска
|ИЗ
| Справочник.Номенклатура КАК Таблица
| " + ТекстУсловия;
Для Каждого КлючИЗначение Из ПараметрыЗапроса Цикл
Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
КонецЦикла;
РезультатЗапроса = Запрос.Выполнить();
В результате сформируется текст запроса с условием вида ГДЕ Таблица.СтрокаПоиска ПОДОБНО &Символ_1 И Таблица.СтрокаПоиска ПОДОБНО &Символ_2, что гарантирует проверку наличия каждого символа независимо от их порядка следования.
Проанализируем более сложную ситуацию, когда обе строки хранятся непосредственно в полях базы данных и сопоставление необходимо выполнить исключительно средствами языка запросов. В запросах 1С нет циклов и регулярных выражений, поэтому для посимвольного разбора используется вспомогательная таблица чисел.
Разберем по шагам логику решения:
Числа.Номер <= ДЛИНАСТРОКИ(Таблица.Строка1).ПОДСТРОКА извлекаем каждый отдельный символ первой строки.ПОДОБНО.Посмотрим на пример текста запроса:
ВЫБРАТЬ 1 КАК Номер ПОМЕСТИТЬ ВТ_Числа
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ИсходныеДанные.Идентификатор КАК Идентификатор,
ИсходныеДанные.Строка1 КАК Строка1,
ИсходныеДанные.Строка2 КАК Строка2,
ПОДСТРОКА(ИсходныеДанные.Строка1, ВТ_Числа.Номер, 1) КАК Символ
ПОМЕСТИТЬ ВТ_Символы
ИЗ
ВТ_ИсходныеДанные КАК ИсходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Числа КАК ВТ_Числа
ПО ВТ_Числа.Номер <= ДЛИНАСТРОКИ(ИсходныеДанные.Строка1)
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ВТ_Символы.Идентификатор КАК Идентификатор
ИЗ
ВТ_Символы КАК ВТ_Символы
ГДЕ
ВТ_Символы.Строка2 ПОДОБНО "%" + ВТ_Символы.Символ + "%"
СГРУППИРОВАТЬ ПО
ВТ_Символы.Идентификатор,
ДЛИНАСТРОКИ(ВТ_Символы.Строка1)
ИМЕЮЩИЕ
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Символы.Символ) = ДЛИНАСТРОКИ(ВТ_Символы.Строка1)
Обратите внимание: для строк неограниченной длины использование строковых функций в некоторых СУБД требует предварительного приведения типов с помощью конструкции ВЫРАЗИТЬ(Поле КАК СТРОКА(N)).
Если выборку данных можно получить без предварительной строгой фильтрации в запросе, гораздо проще и эффективнее выполнить проверку алгоритмически в коде 1С с помощью функций Найти (или СтрНайти) и Сред.
Посмотрим на пример функции проверки:
Функция ВсеСимволыПрисутствуют(Знач СтрокаСимволов, Знач ИсходнаяСтрока)
Для Номер = 1 По СтрДлина(СтрокаСимволов) Цикл
Символ = Сред(СтрокаСимволов, Номер, 1);
Если СтрНайти(ИсходнаяСтрока, Символ) = 0 Тогда
Возврат Ложь; // Хотя бы один символ не найден
КонецЕсли;
КонецЦикла;
Возврат Истина;
КонецФункции
Этот способ работает быстро, не перегружает сервер СУБД сложными объединениями и легко читается другими разработчиками.