При интеграции с внешними источниками, загрузке данных из таблиц Excel или сопоставлении документов контрагентов часто возникает задача поиска документа по короткому номеру (например, "1" или "15"), в то время как в информационной базе 1С номер хранится в полном формате с префиксом и лидирующими нулями (например, "ПР0000001" или "ПР-000015").
Попытка найти документ через условие запроса ПОДОБНО "%" + Номер приводит к двум серьезным проблемам:
ПОДОБНО "%1" найдет не только документ "ПР0000001", но и "ПР0000011", "ПР0000021", "ПР0000101" и так далее.Номер. СУБД выполняет полное сканирование таблицы (Table Scan), что приводит к длительным задержкам и блокировкам данных.Разберем основные варианты решения этой задачи в зависимости от архитектуры вашей базы и требований к производительности.
Если задача возникает периодически (например, при пакетной обработке входящих файлов) и дорабатывать структуру метаданных конфигурации нежелательно, наиболее оптимальным решением является двухэтапная выборка.
Проанализируем алгоритм:
Ссылка и Номер в таблицу значений или массив структур.Посмотрим на пример функции для очистки номера от буквенных префиксов и ведущих нулей:
Функция ПолучитьЧисловойНомер(Знач ИсходныйНомер) Экспорт
// Удаляем пробелы
СтрокаНомера = СокрЛП(ИсходныйНомер);
// Удаляем нецифровые символы с начала строки
Пока СтрДлина(СтрокаНомера) > 0 И Не (КодСимвола(СтрокаНомера, 1) >= 48 И КодСимвола(СтрокаНомера, 1) <= 57) Цикл
СтрокаНомера = Сред(СтрокаНомера, 2);
КонецЦикла;
// Удаляем ведущие нули
Пока СтрДлина(СтрокаНомера) > 1 И Лев(СтрокаНомера, 1) = "0" Цикл
СтрокаНомера = Сред(СтрокаНомера, 2);
КонецЦикла;
Возврат СтрокаНомера;
КонецФункции
Если конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), можно использовать готовый метод модуля ПрефиксацияОбъектовКлиентСервер:
ОчищенныйНомер = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(ДокументСсылка.Номер, Истина, Истина);
Этот метод автоматически удалит префиксы узлов РИБ, префиксы организаций, дефисы и все лидирующие нули.
В современных версиях платформы (начиная с 8.3.23) для очистки номера удобно использовать встроенный механизм регулярных выражений:
Регулярка = Новый РегулярноеВыражение("^[^\d]*0*(\d+)$");
Если Регулярка.Совпадает(ИсходныйНомер) Тогда
Результаты = Регулярка.Найти(ИсходныйНомер);
ОчищенныйНомер = Результаты[0].Группы[0].Значение;
КонецЕсли;
Если поиск по очищенному номеру выполняется постоянно и в больших объемах (высоконагруженный обмен, частый интерактивный поиск), нормализация строк в памяти может создать избыточную нагрузку на процессор. В этом случае правильным архитектурным решением будет однократное сохранение нормализованного значения в базу данных.
Рассмотрим два пути реализации:
ЧисловойНомер) с установленным свойством Индексировать. При записи документа в модуле объекта вычисляйте и заполняйте это поле. Поиск по такому полю в запросе через точное равенство ГДЕ ЧисловойНомер = &ИскомыйНомер будет выполняться мгновенно по индексу.НомераДляПоиска с измерениями Документ (тип ДокументСсылка) и ОчищенныйНомер (тип Строка или Число, ведущее/индексированное). Заполняйте регистр при проведении или записи через подписку на событие ПриЗаписи.Если формат номеров в конкретной базе строго стандартизирован (известна общая длина номера и постоянный префикс), можно пойти от обратного: дополнить короткий номер до полного формата базы перед выполнением запроса.
Посмотрим на пример форматирования короткого номера:
ДлинаЧисловойЧасти = 7; // Например, длина номера 9 символов (2 символа префикса + 7 цифр)
ПрефиксБазы = "ПР";
СтрокаЧисла = Формат(Число(ИскомыйНомер), "ЧЦ=" + ДлинаЧисловойЧасти + "; ЧВН=; ЧГ=");
ПолныйНомерДляПоиска = ПрефиксБазы + СтрокаЧисла;
// Теперь поиск выполняется по строгому равенству с использованием штатного индекса
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг
|ГДЕ
| Номер = &ПолныйНомер
| И Дата МЕЖДУ &ДатаНачала И &ДатаОкончания";
Запрос.УстановитьПараметр("ПолныйНомер", ПолныйНомерДляПоиска);
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаДокумента));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаДокумента));
Важное предостережение: в 1С нумерация документов почти всегда периодическая (в пределах года) и ведется отдельно по каждой организации. Поиск исключительно по номеру без указания периода (года) и организации может привести к ошибочному сопоставлению с документом прошлых лет.