Как в запросе 1С найти число по подстроке?

Программист 1С v8.3 (Обычные формы)
← На главную

При разработке отчетов и обработок часто возникает задача найти элементы справочника или документы по вхождению определенного набора цифр в номер или код. Если поле в метаданных имеет числовой тип (например, Число), стандартная конструкция с оператором ПОДОБНО приводит к ошибкам компиляции или выполнения запроса. Проанализируем, почему возникают эти ошибки, и рассмотрим корректные способы решения задачи.

Почему стандартный запрос завершается с ошибкой

Рассмотрим типичный пример ошибочного кода, в котором предпринимается попытка явного преобразования типа или прямого сравнения числа со строковым шаблоном:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Вагоны.Код КАК Код,
    |	ВЫРАЗИТЬ(Вагоны.Код КАК СТРОКА(8)) КАК ЧислоСтрокой
    |ИЗ
    |	Справочник.ДислокацияВагоныСписок КАК Вагоны
    |ГДЕ
    |	ЧислоСтрокой ПОДОБНО &ШаблонПоиска";

При выполнении подобного кода разработчик сталкивается сразу с несколькими критическими ограничениями платформы 1С:Предприятие:

  1. Назначение функции ВЫРАЗИТЬ: В языке запросов 1С конструкция ВЫРАЗИТЬ не осуществляет конвертацию данных из одного типа в другой (например, из Число в Строка). Она предназначена исключительно для приведения составного типа к конкретному типу или уточнения длины строкового/числового поля.
  2. Ограничения оператора ПОДОБНО: Данный оператор работает строго со строковыми операндами. Если передать в него поле числового типа (например, Вагоны.Код ПОДОБНО &Шаблон), платформа выдаст синтаксическую ошибку «Неверные параметры ПОДОБНО».
  3. Область видимости псевдонимов: Псевдонимы полей, объявленные в секции ВЫБРАТЬ (например, ЧислоСтрокой), недоступны в блоке ГДЕ того же уровня выборки, так как СУБД фильтрует строки до формирования итогового набора полей.
  4. Ограничение функции ПРЕДСТАВЛЕНИЕ: Функция ПРЕДСТАВЛЕНИЕ формирует системное строковое отображение объекта уже после выполнения запроса. Ее результат нельзя использовать в условиях ГДЕ, СГРУППИРОВАТЬ ПО или операциях соединения таблиц.

Способ 1. Изменение типа реквизита в метаданных (рекомендуемый)

Если код справочника или номер документа предназначен для идентификации сущности, а не для математических расчетов, хранить его в числовом виде не рекомендуется. Разберем по шагам правильное архитектурное решение:

  1. Открываем конфигуратор и находим целевой справочник.
  2. Меняем тип реквизита Код (или пользовательского числового реквизита) на Строка требуемой длины.
  3. Применяем изменения в структуре базы данных.

После этого поиск по части номера становится простым и производительным с использованием стандартных параметров запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Вагоны.Код КАК Код,
    |	Вагоны.Ссылка КАК Ссылка
    |ИЗ
    |	Справочник.ДислокацияВагоныСписок КАК Вагоны
    |ГДЕ
    |	Вагоны.Код ПОДОБНО &МаскаПоиска";

Запрос.УстановитьПараметр("МаскаПоиска", "%" + СокрЛП(ЭлементыФормы.ИскомаяЧастьНомераВагона.Значение) + "%");
РезультатЗапроса = Запрос.Выполнить();

Способ 2. Добавление дублирующего строкового реквизита

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

Разберем логику работы этого метода:

  1. Добавляем в справочник реквизит КодСтрокой с типом Строка и включаем для него индексирование.
  2. В модуле объекта в событии ПередЗаписью прописываем автоматическое заполнение:
    
    Процедура ПередЗаписью(Отказ)
    	КодСтрокой = Формат(Код, "ЧГ=0");
    КонецПроцедуры
    
  3. Заполняем реквизит для существующих элементов однократной групповой обработкой.
  4. В запросах строим поиск исключительно по новому строковому полю.

Способ 3. Постобработка результатов на встроенном языке

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

Посмотрим на пример с использованием функции СтрНайти:


ИскомаяПодстрока = СокрЛП(ЭлементыФормы.ИскомаяЧастьНомераВагона.Значение);

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Вагоны.Ссылка КАК Ссылка,
    |	Вагоны.Код КАК Код
    |ИЗ
    |	Справочник.ДислокацияВагоныСписок КАК Вагоны";

ТаблицаВагонов = Запрос.Выполнить().Выгрузить();
ТаблицаРезультатов = ТаблицаВагонов.СкопироватьКолонки();

Для Каждого Стр Из ТаблицаВагонов Цикл
	КодСтрокой = Формат(Стр.Код, "ЧГ=0");
	Если СтрНайти(КодСтрокой, ИскомаяПодстрока) > 0 Тогда
		ЗаполнитьЗначенияСвойств(ТаблицаРезультатов.Добавить(), Стр);
	КонецЕсли;
КонецЦикла;

Способ 4. Преобразование числа в строку внутри запроса через разряды

В исключительных случаях, когда необходимо получить отфильтрованные данные исключительно средствами одного SQL-запроса без изменения метаданных, применяют разложение числа на отдельные цифровые разряды с последующей конкатенацией подстрок.

Рассмотрим пример формирования 5-значного строкового представления числа в теле запроса:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	Вагоны.Ссылка КАК Ссылка,
    |	Вагоны.Код КАК Код,
    |	ПОДСТРОКА(""0123456789"", 1 + (ВЫРАЗИТЬ(Вагоны.Код / 10000 - 0.5 КАК ЧИСЛО(14, 0))) - (ВЫРАЗИТЬ(Вагоны.Код / 100000 - 0.5 КАК ЧИСЛО(14, 0))) * 10, 1) +
    |	ПОДСТРОКА(""0123456789"", 1 + (ВЫРАЗИТЬ(Вагоны.Код / 1000 - 0.5 КАК ЧИСЛО(14, 0))) - (ВЫРАЗИТЬ(Вагоны.Код / 10000 - 0.5 КАК ЧИСЛО(14, 0))) * 10, 1) +
    |	ПОДСТРОКА(""0123456789"", 1 + (ВЫРАЗИТЬ(Вагоны.Код / 100 - 0.5 КАК ЧИСЛО(14, 0))) - (ВЫРАЗИТЬ(Вагоны.Код / 1000 - 0.5 КАК ЧИСЛО(14, 0))) * 10, 1) +
    |	ПОДСТРОКА(""0123456789"", 1 + (ВЫРАЗИТЬ(Вагоны.Код / 10 - 0.5 КАК ЧИСЛО(14, 0))) - (ВЫРАЗИТЬ(Вагоны.Код / 100 - 0.5 КАК ЧИСЛО(14, 0))) * 10, 1) +
    |	ПОДСТРОКА(""0123456789"", 1 + Вагоны.Код - (ВЫРАЗИТЬ(Вагоны.Код / 10 - 0.5 КАК ЧИСЛО(14, 0))) * 10, 1) КАК КодСтрокой
    |ПОМЕСТИТЬ ВтВагоныСтрокой
    |ИЗ
    |	Справочник.ДислокацияВагоныСписок КАК Вагоны
    |;
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |	Вт.Ссылка КАК Ссылка,
    |	Вт.Код КАК Код
    |ИЗ
    |	ВтВагоныСтрокой КАК Вт
    |ГДЕ
    |	Вт.КодСтрокой ПОДОБНО &МаскаПоиска";

Запрос.УстановитьПараметр("МаскаПоиска", "%" + СокрЛП(ЭлементыФормы.ИскомаяЧастьНомераВагона.Значение) + "%");
Результат = Запрос.Выполнить();

Важно: Данный метод создает существенную вычислительную нагрузку на процессор СУБД, отключает использование индексов и подходит только для таблиц небольшого размера или временных технических решений.

Безопасность и правила передачи параметров

При формировании запросов с поиском по шаблону нельзя использовать прямую склейку строк текста запроса с пользовательскими данными. Передачу критериев поиска необходимо осуществлять исключительно через метод УстановитьПараметр() объекта Запрос. Это исключает возникновение синтаксических ошибок при вводе спецсимволов и защищает систему от некорректного поведения.

← На главную