Как найти документ в 1С по короткому номеру без префикса и ведущих нулей?

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

При интеграции с внешними источниками, загрузке данных из таблиц Excel или сопоставлении документов контрагентов часто возникает задача поиска документа по короткому номеру (например, "1" или "15"), в то время как в информационной базе 1С номер хранится в полном формате с префиксом и лидирующими нулями (например, "ПР0000001" или "ПР-000015").

Попытка найти документ через условие запроса ПОДОБНО "%" + Номер приводит к двум серьезным проблемам:

  1. Ложные срабатывания: условие ПОДОБНО "%1" найдет не только документ "ПР0000001", но и "ПР0000011", "ПР0000021", "ПР0000101" и так далее.
  2. Падение производительности: использование знака процента в начале маски поиска исключает использование индекса СУБД по полю Номер. СУБД выполняет полное сканирование таблицы (Table Scan), что приводит к длительным задержкам и блокировкам данных.

Разберем основные варианты решения этой задачи в зависимости от архитектуры вашей базы и требований к производительности.

Вариант 1. Двухэтапный поиск с нормализацией номеров в памяти

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

Проанализируем алгоритм:

  1. Ограничиваем выборку документов в запросе жесткими критериями: периодом (например, диапазон дат ± 1 месяц от даты из файла), организацией и контрагентом. Это уменьшит объем данных с сотен тысяч до нескольких записей.
  2. Выбираем поля Ссылка и Номер в таблицу значений или массив структур.
  3. В цикле приводим строковый номер из базы и номер из файла к единому виду (удаляем префиксы, разделители и лидирующие нули) и выполняем точное сопоставление.

Посмотрим на пример функции для очистки номера от буквенных префиксов и ведущих нулей:


Функция ПолучитьЧисловойНомер(Знач ИсходныйНомер) Экспорт
    // Удаляем пробелы
    СтрокаНомера = СокрЛП(ИсходныйНомер);
    
    // Удаляем нецифровые символы с начала строки
    Пока СтрДлина(СтрокаНомера) > 0 И Не (КодСимвола(СтрокаНомера, 1) >= 48 И КодСимвола(СтрокаНомера, 1) <= 57) Цикл
        СтрокаНомера = Сред(СтрокаНомера, 2);
    КонецЦикла;
    
    // Удаляем ведущие нули
    Пока СтрДлина(СтрокаНомера) > 1 И Лев(СтрокаНомера, 1) = "0" Цикл
        СтрокаНомера = Сред(СтрокаНомера, 2);
    КонецЦикла;
    
    Возврат СтрокаНомера;
КонецФункции

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


ОчищенныйНомер = ПрефиксацияОбъектовКлиентСервер.НомерНаПечать(ДокументСсылка.Номер, Истина, Истина);

Этот метод автоматически удалит префиксы узлов РИБ, префиксы организаций, дефисы и все лидирующие нули.

В современных версиях платформы (начиная с 8.3.23) для очистки номера удобно использовать встроенный механизм регулярных выражений:


Регулярка = Новый РегулярноеВыражение("^[^\d]*0*(\d+)$");
Если Регулярка.Совпадает(ИсходныйНомер) Тогда
    Результаты = Регулярка.Найти(ИсходныйНомер);
    ОчищенныйНомер = Результаты[0].Группы[0].Значение;
КонецЕсли;

Вариант 2. Добавление индексированного реквизита или регистра сведений

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

Рассмотрим два пути реализации:

  1. Дополнительный реквизит документа: добавьте в документ числовой или строковый реквизит (например, ЧисловойНомер) с установленным свойством Индексировать. При записи документа в модуле объекта вычисляйте и заполняйте это поле. Поиск по такому полю в запросе через точное равенство ГДЕ ЧисловойНомер = &ИскомыйНомер будет выполняться мгновенно по индексу.
  2. Вспомогательный регистр сведений: если менять структуру типовых документов нельзя, создайте независимый непериодический регистр сведений НомераДляПоиска с измерениями Документ (тип ДокументСсылка) и ОчищенныйНомер (тип Строка или Число, ведущее/индексированное). Заполняйте регистр при проведении или записи через подписку на событие ПриЗаписи.

Вариант 3. Форматирование короткого номера под шаблон нумерации

Если формат номеров в конкретной базе строго стандартизирован (известна общая длина номера и постоянный префикс), можно пойти от обратного: дополнить короткий номер до полного формата базы перед выполнением запроса.

Посмотрим на пример форматирования короткого номера:


ДлинаЧисловойЧасти = 7; // Например, длина номера 9 символов (2 символа префикса + 7 цифр)
ПрефиксБазы = "ПР";

СтрокаЧисла = Формат(Число(ИскомыйНомер), "ЧЦ=" + ДлинаЧисловойЧасти + "; ЧВН=; ЧГ=");
ПолныйНомерДляПоиска = ПрефиксБазы + СтрокаЧисла;

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

Запрос.УстановитьПараметр("ПолныйНомер", ПолныйНомерДляПоиска);
Запрос.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаДокумента));
Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаДокумента));

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

← На главную