Форум » [x]Harbour » DBFNTX: как грамотно прервать работу SET FILTER ? » Ответить

DBFNTX: как грамотно прервать работу SET FILTER ?

Sergy: Добрый день Есть некая таблица: date D8 idx1 N5 idx2 N5 flags C5 string C80 И есть фрагмент программы: [pre2]filter_sx := "" SELE table SET FILT TO IIF(LEN(filter_sx)==0,TRUE,(filter_sx $ table->str)) DBEDIT(...)[/pre2] Т.е. пока в строку фильтра ничего не попало - видны все записи. Если юзер хочет отфильтровать часть записей по содержимому строки - срабатывает фильтр. Возникает ситуация: если таблица большая (>100 тыс записей), расположена на сервере и юзер желает увидеть какие-то "редкие" записи, процесс фильтрации начинает занимать непозволительно долгое время. Юзер понимает, что лучше набрать в фильтре что-то другое, но как остановить процесс текущей фильтрации ? Делал так: [pre2]filter_sx := "" SELE table SET FILT TO MyFilter() DBEDIT(...) ... FUNC MyFilter() IF LEN(filter_sx) == 0 RETURN TRUE ELSEIF INKEY(0) == K_ESC // юзер устал ждать ? filter_sx := "" // выключаем фильтр RETURN TRUE ELSE RETURN (filter_sx $ table->string) ENDIF RETURN TRUE[/pre2] Но данный метод приводит вообще к чудным результатам: в 90% случаев после нажатия Esc - DBEDIT() начинает бешено прокручивать список вверх и зависает на первом элементе таблицы. Насколько понял по отладчику, что-то непонятное (для меня) происходит в недрах объекта в районе :Stabilize() С какой стороны к этому вопросу подступиться? PS: в Clipper было тоже самое - но когда тормозило всё, это было не так заметно, а сейчас, на фоне быстрой и адекватной работы Harbour... напрягает....

Ответов - 120, стр: 1 2 3 4 5 6 All

petr707: Построить временный индекс с индексным выражением , содержащим свою функцию, которая реализует фильтр, бегунок создания и будет отслеживать реакцию юзера. Если индекс построился, подключить его к базе.

petr707: Аналогично - копирование таблицы по FOR-условию, которое содержит фильтр, бегунок (для оценки юзером прогноза завершения) Если копирование прошло без прерывания - переоткрыть обзор на новую таблицу (отфильтрованная часть)

Sergy: Ну так получится, что во всех вариантах время реакции на фильтр окажется наибольшим из возможных: нужно будет пройти всю таблицу и выделить все записи, подходящими по условию. В случае фильтра время реакции зависит от частоты попадания удовлетворяющих записей. Например: в table->string слово "Москва" встречается в каждой десятой записи. А слово "Якутия" - один раз на 2000 записей. В случае фильтра по "Москве" отфильтруется в среднем, 200 записей, чтобы 20 из них попали на экран. А для Якутии придется ждать, пока отфильтруется 40 тысяч записей. В случае FOR-условий и тем более, постройки индекса во всех случаях придется ждать обработки всех >100 тыс. записей...


Pasha: Sergy пишет: FUNC MyFilter() IF LEN(filter_sx) == 0 RETURN TRUE ELSEIF INKEY(0) == K_ESC // юзер устал ждать ? filter_sx := "" // выключаем фильтр RETURN TRUE ELSE RETURN (filter_sx $ table->string) ENDIF RETURN TRUE Inkey(0) надо заменить на Inkey() Ну и SetLastKey(0) перед фильтром не забыть поставить

Sergy: Pasha пишет: Inkey(0) надо заменить на Inkey() Ну да, разумеется. Пример писал по памяти. Общий смысл в том, что если юзер нажимает кнопку Esc - нужно выключить фильтр. Ну и SetLastKey(0) перед фильтром не забыть поставить Да, но к сожалению, это не повлияет на результат. DBEDIT() сходит с ума, если ему сначала функция фильтра выдавала FALSE, а потом на тех-же записях - неожиданно - TRUE...

Dima: Как вариант можно по этому полю сделать индекс а затем с помощью DBOI_SKIPWILD или DBOI_SKIPREGEX выбрать номера записей и все потом загнать в BM фильтр , но боюсь что с NTX он не дружит. Можно пример глянуть в Xharbour skipeval.prg , думаю он и в Harbour будет работать.

Andrey: Sergy пишет: процесс фильтрации начинает занимать непозволительно долгое время В Клипере 5.3 фильтр был оптимизирован, даже по сети у меня поиск летал. При переходе на Харбор пришлось отказаться вообще от SET FILTER - по сетке всегда "тормоза", переделал все эти участки программы на условную индексацию. Sergy пишет: В случае FOR-условий и тем более, постройки индекса во всех случаях придется ждать обработки всех >100 тыс. записей... Если уже есть готовое индексное выражение по одному ключу, то даже на базе 100 тыс. записей условная индексация строится МОМЕНТОМ !

Sergy: Dima пишет: Как вариант можно по этому полю сделать индекс а затем с помощью DBOI_SKIPWILD или DBOI_SKIPREGEX выбрать номера записей и все потом загнать в BM фильтр , но боюсь что с NTX он не дружит. Хм, почитал dbinfo.ch - идея занятная, нужно будет попробовать Andrey пишет: В Клипере 5.3 фильтр был оптимизирован, даже по сети у меня поиск летал. При переходе на Харбор пришлось отказаться вообще от SET FILTER - по сетке всегда "тормоза", переделал все эти участки программы на условную индексацию. Если уже есть готовое индексное выражение по одному ключу, то даже на базе 100 тыс. записей условная индексация строится МОМЕНТОМ ! Не очень понятно, если честно. Вот есть таблица, пример в первом сообщении. В строке длиной 80 символов юзер хочет найти ЛЮБОЕ нужное ему слово. А может быть, часть его. Оно может быть в начале, в середине и в конце строки. Как условная индексация сможет тут помочь ?

Andrey: Sergy пишет: Как условная индексация сможет тут помочь ? Держи индекс по этому полю всегда открытым. SELECT table // открыт файл с первым индексом по полю "string" DBSETORDER(1) Условная индексация тогда будет типа: cSlovo := "моск" cIndexTo := "'это поле индекса по базе для сортировки/ ставь сам любое поле" cFileIndex := "table02.cdx" cFilter := "cSlovo $ table->string .AND. !DELETED()" // можно еще условия поставить, допустим cSlovo1 и т.д. DELETEFILE(cFileIndex) // всегда удаляю индекс (CDX) - так как налетаешь потом на грабли SELECT table DBSETORDER(1) INDEX ON &cIndexTo TO (cFileIndex) FOR &cFilter ORDLISTADD( cFileIndex ) // здесь в раб. область базы добавляется второй индекс DbSetOrder(2) nKolRecords := ORDKEYCOUNT() // кол-во найденых записей TBROWSE() //или DBEDIT() Поставь счетчик времени и увидишь сам быстродействие.

petr707: В строке длиной 80 символов юзер хочет найти ЛЮБОЕ нужное ему слово... Видимо, нужно по-другому организовать хранение данных.. полнотекстовый поиск - это не для NTX, это интернет поисковик какой-то

Andrey: Sergy пишет: Например: в table->string слово "Москва" встречается в каждой десятой записи. А слово "Якутия" - один раз на 2000 записей. petr707 пишет: Видимо, нужно по-другому организовать хранение данных.. Наверно нужно делать просто справочник городов, а в базе хранить коды городов, тогда поиск будет просто "летать" !

Sergy: Andrey пишет: Видимо, нужно по-другому организовать хранение данных.. полнотекстовый поиск - это не для NTX, это интернет поисковик какой-то Andrey пишет: Наверно нужно делать просто справочник городов, а в базе хранить коды городов, тогда поиск будет просто "летать" ! поле table->string C80 содержит описание операции, которое формируется автоматически, но может быть скорректировано юзером. Например: При внутрискладском перемещении товара формируется запись: "цех -> отк" или "отк -> приемка" При продаже товара: "магазин:Иванов (Петров)" При поступлении: "склад << поставщик" и тп. к каждому этому описанию юзер может добавить что угодно, например: "/1=переделать документы", "проверить цены", "уточнить оплату" и тд и тп "Москва" и "Якутия" были абстрактными примерами, поясняющими, что часть этих строк может встречаться чаще и фильтр по ним работает быстро, или "нормально", а если ввести часть других - начинается пипец и юзеру проще срубить задачу через диспетчер. Так что проиндексировать точно не получится и используется полнотекстовый поиск, "как в интернете...". Поэтому ищу способ грамотно выключить фильтрацию "на лету".

Andrey: Sergy пишет: Поэтому ищу способ грамотно выключить фильтрацию "на лету". Пробуй, что я предложил выше.

Sergy: Andrey пишет: Пробуй, что я предложил выше. Andrey пишет: Условная индексация тогда будет типа: cSlovo := "моск" cIndexTo := "'это поле индекса по базе для сортировки/ ставь сам любое поле" cFileIndex := "table02.cdx" cFilter := "cSlovo $ table->string .AND. !DELETED()" // можно еще условия поставить, допустим cSlovo1 и т.д. DELETEFILE(cFileIndex) // всегда удаляю индекс (CDX) - так как налетаешь потом на грабли SELECT table DBSETORDER(1) INDEX ON &cIndexTo TO (cFileIndex) FOR &cFilter Один юзер хочет прямо сейчас найти "Моск", юзер на соседнем компе - "Якут" в тоже самое время. Через минуту первому потребовалось "Санкт-П", а другому - "Казань". Через пару минут дальше - "Иваново". Третий юзер в тоже самое время начал искать "Владивосток". Перестраивать индекс каждый раз? Поставь счетчик времени и увидишь сам быстродействие Понятно, что после создания индекса все "залетает"... Но так ведь его создать сначала нужно, те обработать >100 тыс записей. А это время, причем при всех вариантах - максимально долгое из возможных. Или я что-то не так понял ?

Dima: Sergy пишет: Так что проиндексировать точно не получится и используется полнотекстовый поиск, "как в интернете...". Поэтому ищу способ грамотно выключить фильтрацию "на лету". Забей на такую методу Вот тебе живой пример и тормозов ни каких и все влет пашет. [pre2] #include "dbinfo.ch" REQUEST BMDBFNTX Proc main() FIELD F1 local cPattern local amas:={} RDDSETDEFAULT( "BMDBFNTX" ) aeval(directory("_tst.*"),{|x|ferase(x[1])}) dbCreate("_tst", {{"F1", "C", 20, 0}}) USE _tst while lastrec()<100000 dbappend() F1 := strzero(recno(),10)+chr(recno()%26+asc("A")) enddo INDEX ON F1 TO _tst dbcommit() cPattern:="*101*A" dbgotop() if !eof() .and. ! WildMatch(cPattern, ordkeyval()) dborderinfo(DBOI_SKIPWILD,,,cPattern) endif while !eof() if WildMatch(cPattern, ordkeyval()) aadd(amas,recno()) endif dborderinfo(DBOI_SKIPWILD,,,cPattern) enddo // а вот тут в своей проге ты можешь вернуться на нужный тебе индекс BM_DBSETFILTERARRAY(amas) browse() return [/pre2] или можно так и будет еще быстрее [pre2] cRegex:=".*101.*A" cRegex:=HB_REGEXCOMP(cRegex) dbgotop() while !eof() if HB_REGEXHAS(cRegEx,ordkeyval()) aadd(amas,recno()) endif dborderinfo(DBOI_SKIPREGEX,,,cRegex) enddo BM_DBSETFILTERARRAY(amas) browse() [/pre2]

Sergy: Dima пишет: REQUEST BMDBFNTX Никогда не сталкивался с таким зверем. Погуглил на этом форуме и в интернете - мало информации. Подтолкните в правильном направлении плиз. Спасибо.

Dima: Sergy Тема есть про BMDBFCDX в твоем случае BMDBFNTX http://clipper.borda.ru/?1-4-0-00000808-000-0-0-1363106357 А вообще harbour\contrib\rddbm\ ЗЫ То есть если умалчиваемым RDD сделать BMDBFNTX то все будет работать как и работало но добавятся новые возможности с BITMAP фильтрами.

Sergy: Dima Спасибо, оказывается, интересная штука! Пошел плотно изучать. Может кому-то будет интересно - вот что надумал. Если нельзя менять SET FILTER "на лету", значит нужно проанализировать - откуда сейчас идет вызов. [pre2] filter_sx := "" SELE table SET FILT TO MyFilter() DBEDIT(...) ... FUNC MyFilter() LOCAL ps:=ProcStack() // получаем стек процедур в виде строки IF LEN(filter_sx) == 0 RETURN TRUE ENDIF IF INKEY() == K_ESC // юзер устал ждать ? IF ("STAB" $ ps) .OR. ("SKIPPED" $ ps) // СЕЙЧАС НЕЛЬЗЯ ! ELSE filter_sx := "" // выключаем RETURN TRUE ENDIF ENDIF RETURN (filter_sx $ table->string) [/pre2] Работает четко - вырубает фильтр без косяков. Параллельно мониторю стек вызовов. Если окажется еще что-то, кроме "STABILIZE", "FORCESTABLE" и "SKIPPED", что приводит к зацикливанию - легко добавить в условие.

Andrey: Dima пишет: То есть если умалчиваемым RDD сделать BMDBFNTX то все будет работать как и работало но добавятся новые возможности с BITMAP фильтрами. Если база открыта рабочей программой RDDCDX, можно ли открывать эту базу с помощью BMDBFCDX только для поиска ? Косяков никаких не будет ? И еще вопрос про поиск для BMDBFCDX: Есть в базе поля FIO1, FIO2, FIO3 .... FIO12 (C 45) Как организовать поиск сразу по всем полям ? SET FILTER тормозит... Да и условие на 12 полей зашкаливает...

Dima: Andrey пишет: Если база открыта рабочей программой RDDCDX, можно ли открывать эту базу с помощью BMDBFCDX только для поиска ? А зачем так поступать. Сразу дефолтным делай BMDBFCDX , все будет работать как и работало + доп возможности с фильтрами. А вообще если шибко надо то да можно открыть ту же базу в новой рабочей области с BMDBFCDX. Потести в общем ;)



полная версия страницы