При разработке отчетов и обработок часто возникает задача найти элементы справочника или документы по вхождению определенного набора цифр в номер или код. Если поле в метаданных имеет числовой тип (например, Число), стандартная конструкция с оператором ПОДОБНО приводит к ошибкам компиляции или выполнения запроса. Проанализируем, почему возникают эти ошибки, и рассмотрим корректные способы решения задачи.
Рассмотрим типичный пример ошибочного кода, в котором предпринимается попытка явного преобразования типа или прямого сравнения числа со строковым шаблоном:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Вагоны.Код КАК Код,
| ВЫРАЗИТЬ(Вагоны.Код КАК СТРОКА(8)) КАК ЧислоСтрокой
|ИЗ
| Справочник.ДислокацияВагоныСписок КАК Вагоны
|ГДЕ
| ЧислоСтрокой ПОДОБНО &ШаблонПоиска";
При выполнении подобного кода разработчик сталкивается сразу с несколькими критическими ограничениями платформы 1С:Предприятие:
ВЫРАЗИТЬ: В языке запросов 1С конструкция ВЫРАЗИТЬ не осуществляет конвертацию данных из одного типа в другой (например, из Число в Строка). Она предназначена исключительно для приведения составного типа к конкретному типу или уточнения длины строкового/числового поля.ПОДОБНО: Данный оператор работает строго со строковыми операндами. Если передать в него поле числового типа (например, Вагоны.Код ПОДОБНО &Шаблон), платформа выдаст синтаксическую ошибку «Неверные параметры ПОДОБНО».ВЫБРАТЬ (например, ЧислоСтрокой), недоступны в блоке ГДЕ того же уровня выборки, так как СУБД фильтрует строки до формирования итогового набора полей.ПРЕДСТАВЛЕНИЕ: Функция ПРЕДСТАВЛЕНИЕ формирует системное строковое отображение объекта уже после выполнения запроса. Ее результат нельзя использовать в условиях ГДЕ, СГРУППИРОВАТЬ ПО или операциях соединения таблиц.Если код справочника или номер документа предназначен для идентификации сущности, а не для математических расчетов, хранить его в числовом виде не рекомендуется. Разберем по шагам правильное архитектурное решение:
Код (или пользовательского числового реквизита) на Строка требуемой длины.После этого поиск по части номера становится простым и производительным с использованием стандартных параметров запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Вагоны.Код КАК Код,
| Вагоны.Ссылка КАК Ссылка
|ИЗ
| Справочник.ДислокацияВагоныСписок КАК Вагоны
|ГДЕ
| Вагоны.Код ПОДОБНО &МаскаПоиска";
Запрос.УстановитьПараметр("МаскаПоиска", "%" + СокрЛП(ЭлементыФормы.ИскомаяЧастьНомераВагона.Значение) + "%");
РезультатЗапроса = Запрос.Выполнить();
Если тип исходного числового поля изменить нельзя из-за жесткой привязки других механизмов конфигурации, добавим дополнительный строковый реквизит (например, КодСтрокой).
Разберем логику работы этого метода:
КодСтрокой с типом Строка и включаем для него индексирование.ПередЗаписью прописываем автоматическое заполнение:
Процедура ПередЗаписью(Отказ)
КодСтрокой = Формат(Код, "ЧГ=0");
КонецПроцедуры
Если справочник содержит небольшое количество записей (до нескольких тысяч) и менять структуру метаданных нельзя, оптимально перенести фильтрацию из языка запросов во встроенный язык 1С.
Посмотрим на пример с использованием функции СтрНайти:
ИскомаяПодстрока = СокрЛП(ЭлементыФормы.ИскомаяЧастьНомераВагона.Значение);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Вагоны.Ссылка КАК Ссылка,
| Вагоны.Код КАК Код
|ИЗ
| Справочник.ДислокацияВагоныСписок КАК Вагоны";
ТаблицаВагонов = Запрос.Выполнить().Выгрузить();
ТаблицаРезультатов = ТаблицаВагонов.СкопироватьКолонки();
Для Каждого Стр Из ТаблицаВагонов Цикл
КодСтрокой = Формат(Стр.Код, "ЧГ=0");
Если СтрНайти(КодСтрокой, ИскомаяПодстрока) > 0 Тогда
ЗаполнитьЗначенияСвойств(ТаблицаРезультатов.Добавить(), Стр);
КонецЕсли;
КонецЦикла;
В исключительных случаях, когда необходимо получить отфильтрованные данные исключительно средствами одного 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) КАК КодСтрокой
|ПОМЕСТИТЬ ВтВагоныСтрокой
|ИЗ
| Справочник.ДислокацияВагоныСписок КАК Вагоны
|;
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Вт.Ссылка КАК Ссылка,
| Вт.Код КАК Код
|ИЗ
| ВтВагоныСтрокой КАК Вт
|ГДЕ
| Вт.КодСтрокой ПОДОБНО &МаскаПоиска";
Запрос.УстановитьПараметр("МаскаПоиска", "%" + СокрЛП(ЭлементыФормы.ИскомаяЧастьНомераВагона.Значение) + "%");
Результат = Запрос.Выполнить();
Важно: Данный метод создает существенную вычислительную нагрузку на процессор СУБД, отключает использование индексов и подходит только для таблиц небольшого размера или временных технических решений.
При формировании запросов с поиском по шаблону нельзя использовать прямую склейку строк текста запроса с пользовательскими данными. Передачу критериев поиска необходимо осуществлять исключительно через метод УстановитьПараметр() объекта Запрос. Это исключает возникновение синтаксических ошибок при вводе спецсимволов и защищает систему от некорректного поведения.