При работе с большим количеством поставщиков возникает типовая проблема: прайс-листы содержат сотни тысяч или миллионы позиций, которые регулярно обновляются. Если загружать все эти данные напрямую в справочник Номенклатура или даже в подчиненный справочник НоменклатураКонтрагентов, информационная база быстро раздувается до критических размеров, а поиск и выбор товаров пользователями становятся крайне медленными.
Разберем архитектурные варианты хранения прайсов, организацию быстрого поиска и механизм создания номенклатуры «по требованию» (on-demand), когда элемент справочника создается только в момент реального оформления заказа покупателя.
В типовых конфигурациях (например, 1С:Управление торговлей, 1С:Комплексная автоматизация или 1С:ERP) регистрация цен поставщиков завязана на обязательное наличие ссылочного объекта — номенклатуры или номенклатуры поставщика. При работе с прайс-листами на сотни тысяч строк стандартный подход сталкивается со следующими ограничениями:
Оптимальным решением внутри базы 1С является создание отдельного независимого непериодического регистра сведений (например, ПрайсЛистыПоставщиков), в котором все измерения и реквизиты хранятся в виде примитивных типов или ссылок только на поставщиков.
Рассмотрим рекомендуемую структуру регистра:
Поставщик — тип СправочникСсылка.Контрагенты (или СправочникСсылка.Партнеры).ОчищенныйАртикул — тип Строка (фиксированная длина, индексируемое поле для быстрого поиска).Бренд — тип Строка или ссылка на справочник брендов/производителей.Цена — тип Число.Валюта — тип СправочникСсылка.Валюты.Остаток — тип Число (или строковый статус наличия).СрокПоставкиДней — тип Число.ИсходноеНаименование — тип Строка (для отображения пользователю).ИсходныйАртикул — тип Строка (в оригинальном написании).ДатаОбновления — тип Дата.Главное преимущество непериодического регистра заключается в простоте и скорости полной перезаписи данных поставщика. Для обновления прайса не нужно построчно удалять старые записи. Достаточно использовать объект РегистрСведенийНаборЗаписей с отбором по поставщику:
Набор = РегистрыСведений.ПрайсЛистыПоставщиков.СоздатьНаборЗаписей();
Набор.Отбор.Поставщик.Установить(ВыбранныйПоставщик);
// Заполняем набор новыми строками из загруженного файла
Для Каждого СтрокаПрайса Из ТаблицаЗагрузки Цикл
Запись = Набор.Добавить();
Запись.Поставщик = ВыбранныйПоставщик;
Запись.ОчищенныйАртикул = НормализоватьАртикул(СтрокаПрайса.Артикул);
Запись.Бренд = СтрокаПрайса.Бренд;
Запись.ИсходныйАртикул = СтрокаПрайса.Артикул;
Запись.ИсходноеНаименование = СтрокаПрайса.Наименование;
Запись.Цена = СтрокаПрайса.Цена;
Запись.Валюта = СтрокаПрайса.Валюта;
Запись.Остаток = СтрокаПрайса.Количество;
Запись.ДатаОбновления = ТекущаяДатаСеанса();
КонецЦикла;
// Запись с замещением очищает старый прайс поставщика за одну транзакцию
Набор.Записать(Истина);
Поиск по условию ПОДОБНО "%подстрока%" по таблице в 1 000 000 записей приводит к полному сканированию таблицы СУБД (Full Table Scan) и блокировкам. Для обеспечения мгновенного отклика необходимо выполнять нормализацию артикулов как при загрузке прайса, так и при вводе поискового запроса пользователем.
Посмотрим на пример функции нормализации артикула:
Функция НормализоватьАртикул(Знач ВходнаяСтрока) Экспорт
Результат = ВРЕГ(СокрЛП(ВходнаяСтрока));
// Удаляем разделители, спецсимволы и пробелы
СимволыДляУдаления = " -_./\,()[]{}*#";
Для Индекс = 1 По СтрДлина(СимволыДляУдаления) Цикл
Символ = Сред(СимволыДляУдаления, Индекс, 1);
Результат = СтрЗаменить(Результат, Символ, "");
КонецЦикла;
// Заменяем схожие символы кириллицы на латиницу
Результат = СтрЗаменить(Результат, "А", "A");
Результат = СтрЗаменить(Результат, "В", "B");
Результат = СтрЗаменить(Результат, "С", "C");
Результат = СтрЗаменить(Результат, "Е", "E");
Результат = СтрЗаменить(Результат, "Н", "H");
Результат = СтрЗаменить(Результат, "К", "K");
Результат = СтрЗаменить(Результат, "М", "M");
Результат = СтрЗаменить(Результат, "О", "O");
Результат = СтрЗаменить(Результат, "Р", "P");
Результат = СтрЗаменить(Результат, "Т", "T");
Результат = СтрЗаменить(Результат, "Х", "X");
Возврат Результат;
КонецФункции
Благодаря нормализации поиск по измерению ОчищенныйАртикул (с включенным свойством Индексировать) выполняется за доли секунды через точное совпадение или поиск по началу строки (ПОДОБНО "АРТИКУЛ%").
Схема взаимодействия менеджера при подборе товаров из прайсов выглядит следующим образом:
ПрайсЛистыПоставщиков и выводит сводную таблицу предложений от всех поставщиков с их ценами, валютами и остатками.Номенклатура с данным нормализованным артикулом и брендом.Номенклатура, заполняются базовые реквизиты (наименование, артикул, ставка НДС, единица измерения) и фиксируется привязка к номенклатуре поставщика.Пример программного создания номенклатуры при выборе позиции из прайс-листа:
Функция ПолучитьИлиСоздатьНоменклатуруПоПрайсу(СтрокаВыбора)
// 1. Попытка найти существующую номенклатуру по артикулу
ОчищенныйАртикул = НормализоватьАртикул(СтрокаВыбора.ИсходныйАртикул);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| Номенклатура.Ссылка КАК Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Артикул = &Артикул";
Запрос.УстановитьПараметр("Артикул", СтрокаВыбора.ИсходныйАртикул);
Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
Возврат Выборка.Ссылка;
КонецЕсли;
// 2. Если товар не найден — создаем новый элемент
НовыйЭлемент = Справочники.Номенклатура.СоздатьЭлемент();
НовыйЭлемент.Наименование = СтрокаВыбора.ИсходноеНаименование;
НовыйЭлемент.НаименованиеПолное = СтрокаВыбора.ИсходноеНаименование;
НовыйЭлемент.Артикул = СтрокаВыбора.ИсходныйАртикул;
НовыйЭлемент.БазоваяЕдиницаИзмерения = Справочники.КлассификаторЕдиницИзмерения.Штука;
НовыйЭлемент.СтавкаНДС = Перечисления.СтавкиНДС.НДС20;
НовыйЭлемент.Записать();
Возврат НовыйЭлемент.Ссылка;
КонецФункции
Если суммарный объем прайсов превышает несколько миллионов строк или требуется сложный нечеткий поиск (с учетом опечаток и перестановки слов), целесообразно вынести хранение прайсов за пределы основной информационной базы: