Есть ert, который ищет элементы справочника по вхождению в их название заданных пользователем символов и выводит найденное на экран. Примерно так:
Справочник порядка 15 тыс. элементов, один поиск занимает около 20 сек.
Как или чем это ускорить?
Спр=СоздатьОбъект("Справочник.Номенклатура");
Если ВвестиСтроку(стр,"Введите наименование для поиска")=1 тогда
Спр.ВыбратьЭлементы();
Пока спр.ПолучитьЭлемент()=1 цикл
если спр.Этогруппа()=1 тогда
продолжить;
конецесли;
Если Найти(спр.текущийэлемент().наименование,сокрлп(стр))=1 тогда
тз.новаястрока();
тз.товар=спр.ТекущийЭлемент();
КонецЦикла;
Конецесли;
ПоказатьСправочник порядка 15 тыс. элементов, один поиск занимает около 20 сек.
Как или чем это ускорить?
Ответы
Подписаться на ответы
Инфостарт бот
Сортировка:
Древо развёрнутое
Свернуть все
(3) Ну, немножко "причесать" код можно разными способами: сократить количество строк, убрать лишний .ТекущийЭлемент() и вычисления в цикле:
Спр=СоздатьОбъект("Справочник.Номенклатура");
А радикально улучшить быстродействие - через ВК или прямые запросы SQL.
Спр=СоздатьОбъект("Справочник.Номенклатура");
Если ВвестиСтроку(стр,"Введите наименование для поиска")=1 тогда
СокрСтр=СокрЛП(стр);
Спр.ВыбратьЭлементы();
Пока спр.ПолучитьЭлемент()=1 цикл
Если спр.Этогруппа()=0 тогда
Если Найти(спр.наименование,СокрСтр)>0 тогда
тз.новаястрока();
тз.товар=спр.ТекущийЭлемент();
КонецЕсли;
КонецЕсли;
КонецЦикла;
Конецесли;
ПоказатьА радикально улучшить быстродействие - через ВК или прямые запросы SQL.
(4) Cooler,
Подозреваю, что ваш вариант прилично ускорится, если строки
Если спр.Этогруппа()=0 тогда
Если Найти(спр.наименование,СокрСтр)>0 тогда
переставить местами.
Ну и не мешало бы наименование и образец поиска приводить к верхнему регистру, что бы "Батон" и "батон" были совпадением.
Подозреваю, что ваш вариант прилично ускорится, если строки
Если спр.Этогруппа()=0 тогда
Если Найти(спр.наименование,СокрСтр)>0 тогда
переставить местами.
Ну и не мешало бы наименование и образец поиска приводить к верхнему регистру, что бы "Батон" и "батон" были совпадением.
(6)
Разве что кроме случая, когда групп в справочнике больше, чем элементов и большинство наименований групп включают строку поиска.
Вероятность этого крайне мала, так что плюсую.
Подозреваю, что ваш вариант прилично ускорится, если строки
Если спр.Этогруппа()=0 тогда
Если Найти(спр.наименование,СокрСтр)>0 тогда
переставить местами.
Очень даже может быть, это я не додумал.
Если спр.Этогруппа()=0 тогда
Если Найти(спр.наименование,СокрСтр)>0 тогда
переставить местами.
Разве что кроме случая, когда групп в справочнике больше, чем элементов и большинство наименований групп включают строку поиска.
Вероятность этого крайне мала, так что плюсую.
Ну и не мешало бы наименование и образец поиска приводить к верхнему регистру
А вот целесообразность этого зависит от точной постановки задачи, чем автор нас не балует.
для поиска по первым символам, можно в запросе указать Условие ( наименование >= искстрока) Условие ( наименование < искстрока2 )
где искстрока2 заменой последнего символа на символ с кодом на 1 больше.
Пример: если нужно найти абв то искстрока = "абв"; искстрока2 = "абг";
для скл версии выгрыш в скорости будет ощутимый.
для дбф - не знаю.
где искстрока2 заменой последнего символа на символ с кодом на 1 больше.
Пример: если нужно найти абв то искстрока = "абв"; искстрока2 = "абг";
для скл версии выгрыш в скорости будет ощутимый.
для дбф - не знаю.
(40) Cooler, действительно принесло результат, т.к. я не стал весь код сюда писать, далее за было и т.д. поэтому в моем таки случае было нужно. Прошу прощения за ввод в заблуждение путем выкладки неполного кода.
ТОвар=СПрТов.ТекущийЭлемент();
тз.НоваяСтрока()
тз.цена=СПрТов.ТекущийЭлемент().цена;
тз.производитель=СПрТов.ТекущийЭлемент().Производитель
(41)
А теперь попробуйте:
Surprise!
P.S. Хотя ХЗ, что там у вас за "и т.д.", может, и нужно. Но сомневаюсь я, функция ТекущийЭлемент() имеет узко специфическое назначение - передать ссылку на элемент справочника в какую-либо процедуру или функцию. А для обращения к элементу выборки и его реквизитам она нужна лишь для... замедления работы программы, что у вас лучше всего остального получается.
поэтому в моем таки случае было нужно
Ффух!
А теперь попробуйте:
тз.цена=СПрТов.цена;
тз.производитель=СПрТов.Производитель;
Surprise!
P.S. Хотя ХЗ, что там у вас за "и т.д.", может, и нужно. Но сомневаюсь я, функция ТекущийЭлемент() имеет узко специфическое назначение - передать ссылку на элемент справочника в какую-либо процедуру или функцию. А для обращения к элементу выборки и его реквизитам она нужна лишь для... замедления работы программы, что у вас лучше всего остального получается.
(14) vitebsk13, только запросом.
Либо надо определиться в какой области справочника искать. Например, поиск только внутри определенной группы ускорит процесс. Сейчас у тебя перебирается весь справочник. Более логично искать что-то в нужном месте, а не везде. Например: искать болты лучше в папке "метизы", если таковая иерархия имеет место быть.
Либо надо определиться в какой области справочника искать. Например, поиск только внутри определенной группы ускорит процесс. Сейчас у тебя перебирается весь справочник. Более логично искать что-то в нужном месте, а не везде. Например: искать болты лучше в папке "метизы", если таковая иерархия имеет место быть.
(14) vitebsk13, ну во-первых непонятно каким советам вы последовали так что присмотритесь к (17), во-вторых чтобы посоветовать вам что то по железу, неплохо бы указать его конфигурацию и нагрузку (кол-во пользователей 1с, доп сервисы винды на данном сервере, например установленный sql даст такую нагрузку на диск что 1с нервно курит в сторонке). А так могу сказать чем мощнее - тем лучше, ставьте аппаратный райд 5 или 10 с большим кэшем на ссд высокой надежности, чем больше-тем лучше, и xeon E3 или E5 в зависимости от количества пользователей.
Попробуйте, может так будет быстрее:
Если ВвестиСтроку(стр,"Введите наименование для поиска",100) = 1 тогда
СокрСтр = Нрег(СокрЛП(стр));
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Номенклатура = Справочник.Номенклатура.ТекущийЭлемент;
|Наименование = Справочник.Номенклатура.Наименование;
|Группировка Номенклатура без групп;
//|Группировка Наименование;
|Условие(Найти(Нрег(Наименование),СокрСтр)>0);
|"//}}ЗАПРОС
;
// Если ошибка в запросе, то выход из процедуры
Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
Возврат;
КонецЕсли;
ТЗ = СоздатьОбъект("ТаблицаЗначений");
Запрос.Выгрузить(ТЗ,0,0);
ТЗ.ВыбратьСтроку();
КонецЕсли;
Показать
(19) pvase, нужно попробовать)
Вот код:
Вот код:
Если ВвестиСтроку(Наим,"Введите строку поиска",25,0)=1 Тогда
наим=нрег(СокрЛП(наим));
СпрТов=СоздатьОбъект("Справочник.Номенклатура");
СпрТов.ВыбратьЭлементы();
Пока СпрТов.ПолучитьЭлемент() = 1 Цикл
ТОвар=СПрТов.ТекущийЭлемент();
Если Найти(Нрег(Товар.Наименование),Наим)<>0 Тогда
Если Товар.ЭтоГРуппа()=0 тогда
Если Товар.ПометкаУдаления()=0 тогда
тз.новаястрока();
тз.товар=товар;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Конецесли;
Показать
(23) Cooler, Он все правильно делает. Спр.ТекущийЭлемент() - производит чтение записей по ID из таблицы справочника. Присвоение переменной - позволяет уйти от второго чтения тех же записей при обращении к справочнику по ТекущийЭлемент();. Т.е. каждое обращение к переменной Товар не будет производить поиск элемента в справочнике по ID, в отличие от ТекущийЭлемент(), который каждый раз производит чтение таблицы и поиск элемента по полю ID.
Можно так:
Если ВвестиСтроку(Наим,"Введите строку поиска",25,0)=1 Тогда
наим=нрег(СокрЛП(наим));
СпрТов = СоздатьОбъект("Справочник.Номенклатура");
// если процедура в модуле формы списка
Если ИерархическийСписок(,) = 1 Тогда
СпрТов.ИспользоватьРодителя( ?(ЭтоГруппа() = 1, ТекущийЭлемент(),ТекущийЭлемент().Родитель) );
КонецЕсли;
СпрТов.ВыбратьЭлементы();
Пока СпрТов.ПолучитьЭлемент() = 1 Цикл
Если СпрТов.ЭтоГруппа() + СпрТов.ПометкаУдаления() > 0 Тогда
Продолжить;
КонецЕсли;
Если Найти(Нрег(Товар.Наименование), Наим) > 0 Тогда
тз.новаястрока();
тз.товар=товар;
КонецЕсли;
КонецЦикла;
Конецесли;
ПоказатьПрисвоение переменной - позволяет уйти от второго чтения тех же записей при обращении к справочнику по ТекущийЭлемент()
Может, вы не заметили, но Спр.Наименование, Спр.ЭтоГруппа(), да и Спр.ПометкаУдаления() обходятся без второго чтения, оно осуществляется только при нахождении искомых элементов. В таких случаях да, мой код помедленнее отработает.
Зато в нем не будет вычисления Спр.ТекущийЭлемент() на каждом элементе, попадет он потом в выборку или нет.
Впрочем, без замера производительности это все уже пустые разговоры, так что я закругляюсь.
Вот ваш запрос может дать существенно другие результаты за счет переноса процесса перебора из интерпретируемого кода внутрь движка 1С, а будут они лучше или хуже - ХЗ.
P.S. Интересно, автор учитывает, что при втором и последующих запусках обработки она может отрабатывать быстрее в разы за счет того, что справочник попал в кэш?
Cooler Согласен конкретно в этом примере это лишнее. Лучше тогда:
Но надо проверить какой алгоритм будет быстрее через перебор или через запрос 1С (если не хочется делать через прямой запрос в базе).
Если ВвестиСтроку(Наим,"Введите строку поиска",25,0)=1 Тогда
наим=нрег(СокрЛП(наим));
СпрТов=СоздатьОбъект("Справочник.Номенклатура");
СпрТов.ВыбратьЭлементы();
Пока СпрТов.ПолучитьЭлемент() = 1 Цикл
Если Найти(Нрег(СпрТов.Наименование),Наим)<>0 Тогда
Если (СпрТов.ЭтоГРуппа()=0) И (СпрТов.ПометкаУдаления()=0) тогда
тз.новаястрока();
тз.товар=СПрТов.ТекущийЭлемент();;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Конецесли;
ПоказатьНо надо проверить какой алгоритм будет быстрее через перебор или через запрос 1С (если не хочется делать через прямой запрос в базе).
(27)
С другой стороны, если как в (22) вложить вторую проверку в первую, то это приводит к появлению дополнительных Если ... КонецЕсли.
И неизвестно, что движок 1С отработает быстрее.
И, уж если занудствовать по поводу оптимизации до упора, то надо делать:
Найти() не может дать отрицательного результата, и незачем заставлять прогу делать пустяковую, но тоже лишнюю проверку.
Если (СпрТов.ЭтоГРуппа()=0) И (СпрТов.ПометкаУдаления()=0) тогда
Насчет вот этого у меня есть сомнения. С одной стороны, для максимального ускорения я против объединения этих условий: если ЭтоГруппа()=1, то проверять на удаление уже излишне, а в таком варианте эта проверка будет выполняться.
С другой стороны, если как в (22) вложить вторую проверку в первую, то это приводит к появлению дополнительных Если ... КонецЕсли.
И неизвестно, что движок 1С отработает быстрее.
И, уж если занудствовать по поводу оптимизации до упора, то надо делать:
// Если Найти(Нрег(СпрТов.Наименование),Наим)<>0 Тогда
Если Найти(Нрег(СпрТов.Наименование),Наим)>0 Тогда
Найти() не может дать отрицательного результата, и незачем заставлять прогу делать пустяковую, но тоже лишнюю проверку.
(28) 1c77 выполняет все условия в выражении И
т.е если вы напишите
Если (0 = 1) И (Функция1() = 1) И (Функция2() = 1) И (Функция3() = 1) Тогда
1с все равно вычислит все три функции.
так что вложеные Если лучше
еще есть ? но с ним текст совсем становиться нечитаемым
т.е если вы напишите
Если (0 = 1) И (Функция1() = 1) И (Функция2() = 1) И (Функция3() = 1) Тогда
1с все равно вычислит все три функции.
так что вложеные Если лучше
еще есть ? но с ним текст совсем становиться нечитаемым
(29) _Z1, Так что бы правильно рассчитать результат выражения ((Функция1()=Чему-то) И (Функция2()=Чему-то) И (Функция3()=Чему-то))
какраз таки и необходимо получить значение всех 3 функций. Вот если бы И поменять на ИЛИ тогда да, после первого же верного условия Функция()=Чему-то, можно прекращать дальнейший расчет функций.
какраз таки и необходимо получить значение всех 3 функций. Вот если бы И поменять на ИЛИ тогда да, после первого же верного условия Функция()=Чему-то, можно прекращать дальнейший расчет функций.
(33) tdr1225, а есть НАСТОЛЬКО умные компиляторы, что икс+0=0 будут вам ругаться, что код не оптимизированный?
кх-м, не слышал о таких... даже для компилятора, что икс+0=0, что икс+7=7 - выполняется одинаково
а автору топика: бредовая реализация бредовой задачи, как я понимаю, кто-то умный захотел, чтобы набрав "кра", получить все ТМЦ, в которых есть эта подстрока. Так подскажу решение, пользователям, которые вносят новые название ТМЦ отбить руки, а потом за КАЖДУЮ ошибку не так введеное название штрафовать, тогда и проблема решиться в течении срока "до следующей зарплаты"
кх-м, не слышал о таких... даже для компилятора, что икс+0=0, что икс+7=7 - выполняется одинаково
а автору топика: бредовая реализация бредовой задачи, как я понимаю, кто-то умный захотел, чтобы набрав "кра", получить все ТМЦ, в которых есть эта подстрока. Так подскажу решение, пользователям, которые вносят новые название ТМЦ отбить руки, а потом за КАЖДУЮ ошибку не так введеное название штрафовать, тогда и проблема решиться в течении срока "до следующей зарплаты"
а есть НАСТОЛЬКО умные компиляторы, что икс+0=0 будут вам ругаться, что код не оптимизированный
неточно цитируешь. Перед этим сравнением выполняется икс=0;
И именно умный компилятор увидит, что условие верно _всегда_, и в скомпилированном коде сравнение будет отсутствовать.
Компилятор не будет ругаться на неоптимальный код, он просто его соптимизирует.
(36) Поскольку при выполнении модуля 1с происходит не интерпретация, а трансляция исходного кода в, по всей видимости, байт-код, и только потом исполнение, 1с подходит под определение компилятора. Если бы 1с была чистым интерпретатором, не могло бы существовать КЗК для 7.7 и поставки без исходных кодов для 8.х
для дбф базы, заготовка
.
//========================================================== ============
Функция ПоискПоПодстрокеSQlite()
//если здесь - значит на периферии
//примем административное правило, на периферии - ДБФ базы
Попытка
глВКлайт.Открыть(":memory:");
Запрос = глВКлайт.НовыйЗапрос();
Запрос.ВыполнитьЗапрос("create virtual table Товары using dbeng(Справочник.Номенклатура)");
Исключение
//тОшибка = "инфо: проблема при выполнении запроса инициализации вирт.таблицы, при случае - сообщите программисту: "+ОписаниеОшибки();
//Сообщить(тОшибка);
Возврат 0;
КонецПопытки;
//ОТЛАДКА
//Сообщить("работаем SQLite");
РазбиваемаяСтрока = ПодСтрокаПоиска;
РазбиваемаяСтрока = СтрЗаменить(РазбиваемаяСтрока,"'","");
РазбиваемаяСтрока = СтрЗаменить(РазбиваемаяСтрока,"""","");
РазбиваемаяСтрока = СтрЗаменить(СокрЛП(РазбиваемаяСтрока)," ",РазделительСтрок);
ТекстЗапроса = "SELECT
|Товары.id [Элемент :Справочник.Номенклатура]
|FROM Товары
|WHERE Товары.isfolder=2 and Товары.ismark <> '*'";
Для Индекс = 1 По СтрКоличествоСтрок(РазбиваемаяСтрока)
Цикл Слово = Врег(СтрПолучитьСтроку(РазбиваемаяСтрока,Индекс));
Если ПустоеЗначение(Слово)=1
Тогда Продолжить;
КонецЕсли;
ТекстЗапроса = ТекстЗапроса+"
|AND UPPER(Товары.descr) LIKE '%"+Слово+"%'";
КонецЦикла;
//ТекстЗапроса = ТекстЗапроса+"
//|ORDER BY Элемент.descr";
ТЗраб = СоздатьОбъект("ТаблицаЗначений");
Попытка
_время1 = _GetPerformanceCounter();
Запрос.ВыполнитьЗапрос(ТекстЗапроса).Выгрузить(ТЗраб);
//Сообщить((_GetPerformanceCounter()-_время1)/1000);
//ч=1/0;
Исключение
глВКлайт.Закрыть();
тОшибка = "ошибка при выполнении запроса, сообщите программисту: "+ОписаниеОшибки();
Сообщить(тОшибка);
Возврат 0;
КонецПопытки;
глВКлайт.Закрыть();
.
//==========================================================
Функция ПоискПоПодстрокеSQlite()
//если здесь - значит на периферии
//примем административное правило, на периферии - ДБФ базы
Попытка
глВКлайт.Открыть(":memory:");
Запрос = глВКлайт.НовыйЗапрос();
Запрос.ВыполнитьЗапрос("create virtual table Товары using dbeng(Справочник.Номенклатура)");
Исключение
//тОшибка = "инфо: проблема при выполнении запроса инициализации вирт.таблицы, при случае - сообщите программисту: "+ОписаниеОшибки();
//Сообщить(тОшибка);
Возврат 0;
КонецПопытки;
//ОТЛАДКА
//Сообщить("работаем SQLite");
РазбиваемаяСтрока = ПодСтрокаПоиска;
РазбиваемаяСтрока = СтрЗаменить(РазбиваемаяСтрока,"'","");
РазбиваемаяСтрока = СтрЗаменить(РазбиваемаяСтрока,"""","");
РазбиваемаяСтрока = СтрЗаменить(СокрЛП(РазбиваемаяСтрока)," ",РазделительСтрок);
ТекстЗапроса = "SELECT
|Товары.id [Элемент :Справочник.Номенклатура]
|FROM Товары
|WHERE Товары.isfolder=2 and Товары.ismark <> '*'";
Для Индекс = 1 По СтрКоличествоСтрок(РазбиваемаяСтрока)
Цикл Слово = Врег(СтрПолучитьСтроку(РазбиваемаяСтрока,Индекс));
Если ПустоеЗначение(Слово)=1
Тогда Продолжить;
КонецЕсли;
ТекстЗапроса = ТекстЗапроса+"
|AND UPPER(Товары.descr) LIKE '%"+Слово+"%'";
КонецЦикла;
//ТекстЗапроса = ТекстЗапроса+"
//|ORDER BY Элемент.descr";
ТЗраб = СоздатьОбъект("ТаблицаЗначений");
Попытка
_время1 = _GetPerformanceCounter();
Запрос.ВыполнитьЗапрос(ТекстЗапроса).Выгрузить(ТЗраб);
//Сообщить((_GetPerformanceCounter()-_время1)/1000);
//ч=1/0;
Исключение
глВКлайт.Закрыть();
тОшибка = "ошибка при выполнении запроса, сообщите программисту: "+ОписаниеОшибки();
Сообщить(тОшибка);
Возврат 0;
КонецПопытки;
глВКлайт.Закрыть();
Для получения уведомлений об ответах подключите телеграм бот:
Инфостарт бот