В ходе работы над конфигурацией заказчика столкнулись с проблемой медленного поиска в больших справочниках. Число элементов в справочниках в данный момент зашкаливает за 300 тысяч. Поиск стандартными средствами по первым буквам занимает до 10 секунд. Поиск подстроки вообще очень долго. В общем-то типовая задача для тех, кто занимается разработкой на 7-ке в SQL. И в общем-то эта задача уже решалась не раз - с помощью 1С++ или с помощью других средств. В каком-то смысле это изобретение велосипеда. В основном заметка полезна пользователям библиотеки ToySQL (хотя и на 1С++ это легко переносится). Особенность работы процедуры - поиск по подстроке в реквизитах наименование, код, а также в реквизитах с сортировкой. Есть возможность поиска по агрегатным реквизитам (поиск через точку). Результаты поиска отображаются в форме списка с помощью установки фильтра по найденным элментам, тем самым можно произвести выбор из этого списка без дополнительных выкрутасов. Встроить в модуль формы списка достаточно просто - добавляем на форму кнопку поиска с вызовом:
глБыстрыйПоиск(Контекст,Синонимы,,ДопРеквизиты)
Текст основной и вспомогательных процедур:
Функция ПолучитьСиноним(Синоним,Синонимы) Если Синонимы = 0 Тогда Возврат Синоним; Иначе Стр = Синонимы.Получить(Синоним); Если ПустаяСтрока(Стр) = 1 Тогда Возврат Синоним; Иначе Возврат Стр; КонецЕсли; КонецЕсли; КонецФункции
Функция ИсключитьИзСписка(Рекв,Список) Если Список = 0 Тогда Возврат 0; Иначе Если Список.НайтиЗначение(Рекв) > 0 Тогда Возврат 1; КонецЕсли; КонецЕсли; Возврат 0; КонецФункции
Процедура глБыстрыйПоиск(Конт,Синонимы=0,Исключить=0,ДопРеквизиты=0) Экспорт РеквизитыПоиска = СоздатьОбъект("СписокЗначений"); Вид = Конт.Вид(); Мета = Метаданные.Справочник(Вид); Если Мета.ДлинаНаименования > 0 Тогда Если ИсключитьИзСписка("Наименование",Исключить) = 0 Тогда РеквизитыПоиска.ДобавитьЗначение("Наименование",ПолучитьСиноним("Наименование",Синонимы)); КонецЕсли; КонецЕсли; Если (Мета.ДлинаКода > 0) И (Мета.ТипКода = "Текстовый") Тогда Если ИсключитьИзСписка("Код",Исключить) = 0 Тогда РеквизитыПоиска.ДобавитьЗначение("Код",ПолучитьСиноним("Код",Синонимы)); КонецЕсли; КонецЕсли;
Для н=1 По Мета.Реквизит() Цикл Рекв = Мета.Реквизит(н); Если Рекв.Сортировка = 1 Тогда Если Рекв.Тип = "Строка" Тогда Если ИсключитьИзСписка(Рекв.Идентификатор,Исключить) = 0 Тогда РеквизитыПоиска.ДобавитьЗначение(Рекв.Идентификатор,?(ПустоеЗначение(Рекв.Синоним)=1,Рекв.Идентификатор,Рекв.Синоним)); КонецЕсли; КонецЕсли; КонецЕсли; КонецЦикла;
Если ПустоеЗначение(ДопРеквизиты) = 0 Тогда Стр = ""; Для н=1 По ДопРеквизиты.РазмерСписка() Цикл Зн = ДопРеквизиты.ПолучитьЗначение(н,Стр); РеквизитыПоиска.ДобавитьЗначение(Зн,Стр); КонецЦикла; КонецЕсли;
Зн = ""; Если РеквизитыПоиска.ВыбратьЗначение(Зн,,,,1) = 1 Тогда Стр = ""; Если ВвестиСтроку(Стр,"Введите строку поиска",50) = 1 Тогда п = Найти(Зн,"."); Если п > 0 Тогда Рекв = Лев(Зн,п-1); МетаРекв = Мета.Реквизит(Рекв); Поиск = Рекв+"."+Сред(Зн,п+1);
ТекстЗапроса = "SEL ECT [Спр.Ссылка] |FR OM [Справочник."+Вид+"] Спр WITH (NOLOCK) |JOIN ["+МетаРекв.Тип+"."+МетаРекв.Вид+"] "+Рекв+" WITH (NOLOCK) ON [Спр."+Рекв+"] = ["+Рекв+".Ссылка] |WHERE UPPER(["+Поиск+"]) LIKE '%"+Врег(Стр)+"%' |ORDER BY [Спр.Наименование]";
Иначе ТекстЗапроса = "SEL ECT [Ссылка] FR OM [Справочник."+Вид+"] WITH (NOLOCK) WHERE UPPER(["+Зн+"]) LIKE '%"+Врег(Стр)+"%' |ORDER BY [Наименование]"; КонецЕсли;
Запрос = СоздатьОбъект("ToyQuery"); Запрос.Вернуть1С = 1; Если Запрос.МетаЗапрос(ТекстЗапроса) = 1 Тогда ТЗ = СоздатьОбъект("СписокЗначений"); Запрос.Выгрузить(ТЗ); Если ТЗ.РазмерСписка() > 0 Тогда Конт.ИспользоватьСписокЭлементов(ТЗ); Иначе Сообщить("Ничего не найдено"); Конт.ИспользоватьСписокЭлементов(); КонецЕсли; Иначе Сообщить(Запрос.Ошибка); КонецЕсли; КонецЕсли; КонецЕсли; КонецПроцедуры