Как в запросе 1С проверить вхождение всех символов одной строки в другую с помощью оператора ПОДОБНО?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей IT и автоматизация бизнеса
← На главную

При разработке отчетов и обработок в 1С нередко возникает задача сопоставления строковых данных: например, проверить, присутствуют ли все символы из одной строки (например, "15") внутри другой строки (например, "12345"). Разберем, как правильно реализовать такую проверку в запросе и во встроенном языке, а также проанализируем ограничения оператора ПОДОБНО.

Особенности работы оператора ПОДОБНО и квадратных скобок

Частая ошибка при решении этой задачи — попытка использовать квадратные скобки в шаблоне: ПОДОБНО "%[15]%". Важно помнить о правилах языка запросов 1С:

  1. Конструкция [...] означает вхождение хотя бы одного символа из указанного перечня, а не всех символов одновременно. Шаблон "%[15]%" вернет истину и для строки "100", и для "500", даже если второго символа там нет.
  2. Если маска поиска содержит знак процента в начале ("%..."), СУБД не может использовать индексы, что приводит к полному сканированию таблицы (Table Scan). На больших объемах данных это существенно снижает производительность.
  3. Если в проверяемой строке могут встретиться специальные служебные символы языка запросов (%, _, [, ], ^), шаблон вызовет ошибку или сработает неверно. В таких случаях обязательно экранирование через конструкцию СПЕЦСИМВОЛ.

Вариант 1. Динамическое формирование текста запроса во встроенном языке

Рассмотрим наиболее гибкий и быстрый по производительности способ, когда исходная строка-шаблон известна до выполнения запроса. Мы можем разбить строку на отдельные символы во встроенном языке и сформировать цепочку условий через оператор И.

Разберем по шагам реализацию этого подхода:


// Исходные данные для проверки
СтрокаСимволов = "15";

МассивУсловий = Новый Массив;
ПараметрыЗапроса = Новый Структура;

Для Номер = 1 По СтрДлина(СтрокаСимволов) Цикл
    ТекущийСимвол = Сред(СтрокаСимволов, Номер, 1);
    
    // Имя параметра для каждого символа
    ИмяПараметра = "Символ_" + Формат(Номер, "ЧГ=0");
    
    МассивУсловий.Добавить("Таблица.СтрокаПоиска ПОДОБНО &" + ИмяПараметра);
    ПараметрыЗапроса.Вставить(ИмяПараметра, "%" + ТекущийСимвол + "%");
КонецЦикла;

// Собираем итоговое условие ГДЕ
Если МассивУсловий.Количество() > 0 Тогда
    ТекстУсловия = "ГДЕ " + СтрСоединить(МассивУсловий, " И ");
Иначе
    ТекстУсловия = "";
КонецЕсли;

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Таблица.Ссылка КАК Ссылка,
    |	Таблица.СтрокаПоиска КАК СтрокаПоиска
    |ИЗ
    |	Справочник.Номенклатура КАК Таблица
    | " + ТекстУсловия;

Для Каждого КлючИЗначение Из ПараметрыЗапроса Цикл
    Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
КонецЦикла;

РезультатЗапроса = Запрос.Выполнить();

В результате сформируется текст запроса с условием вида ГДЕ Таблица.СтрокаПоиска ПОДОБНО &Символ_1 И Таблица.СтрокаПоиска ПОДОБНО &Символ_2, что гарантирует проверку наличия каждого символа независимо от их порядка следования.

Вариант 2. Проверка связки полей целиком в запросе

Проанализируем более сложную ситуацию, когда обе строки хранятся непосредственно в полях базы данных и сопоставление необходимо выполнить исключительно средствами языка запросов. В запросах 1С нет циклов и регулярных выражений, поэтому для посимвольного разбора используется вспомогательная таблица чисел.

Разберем по шагам логику решения:

  1. Формируем временную таблицу чисел от 1 до максимально возможной длины строки.
  2. Соединяем основную таблицу с таблицей чисел по условию Числа.Номер <= ДЛИНАСТРОКИ(Таблица.Строка1).
  3. С помощью функции ПОДСТРОКА извлекаем каждый отдельный символ первой строки.
  4. Проверяем вхождение выделенного символа во вторую строку через ПОДОБНО.
  5. Группируем результат и сравниваем количество совпадений с длиной исходной строки.

Посмотрим на пример текста запроса:


ВЫБРАТЬ 1 КАК Номер ПОМЕСТИТЬ ВТ_Числа
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВСЕ ВЫБРАТЬ 5
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ИсходныеДанные.Идентификатор КАК Идентификатор,
	ИсходныеДанные.Строка1 КАК Строка1,
	ИсходныеДанные.Строка2 КАК Строка2,
	ПОДСТРОКА(ИсходныеДанные.Строка1, ВТ_Числа.Номер, 1) КАК Символ
ПОМЕСТИТЬ ВТ_Символы
ИЗ
	ВТ_ИсходныеДанные КАК ИсходныеДанные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_Числа КАК ВТ_Числа
		ПО ВТ_Числа.Номер <= ДЛИНАСТРОКИ(ИсходныеДанные.Строка1)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВТ_Символы.Идентификатор КАК Идентификатор
ИЗ
	ВТ_Символы КАК ВТ_Символы
ГДЕ
	ВТ_Символы.Строка2 ПОДОБНО "%" + ВТ_Символы.Символ + "%"
СГРУППИРОВАТЬ ПО
	ВТ_Символы.Идентификатор,
	ДЛИНАСТРОКИ(ВТ_Символы.Строка1)
ИМЕЮЩИЕ
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ВТ_Символы.Символ) = ДЛИНАСТРОКИ(ВТ_Символы.Строка1)

Обратите внимание: для строк неограниченной длины использование строковых функций в некоторых СУБД требует предварительного приведения типов с помощью конструкции ВЫРАЗИТЬ(Поле КАК СТРОКА(N)).

Вариант 3. Проверка во встроенном языке (BSL)

Если выборку данных можно получить без предварительной строгой фильтрации в запросе, гораздо проще и эффективнее выполнить проверку алгоритмически в коде 1С с помощью функций Найти (или СтрНайти) и Сред.

Посмотрим на пример функции проверки:


Функция ВсеСимволыПрисутствуют(Знач СтрокаСимволов, Знач ИсходнаяСтрока)
    
    Для Номер = 1 По СтрДлина(СтрокаСимволов) Цикл
        Символ = Сред(СтрокаСимволов, Номер, 1);
        
        Если СтрНайти(ИсходнаяСтрока, Символ) = 0 Тогда
            Возврат Ложь; // Хотя бы один символ не найден
        КонецЕсли;
    КонецЦикла;
    
    Возврат Истина;
    
КонецФункции

Этот способ работает быстро, не перегружает сервер СУБД сложными объединениями и легко читается другими разработчиками.

← На главную