Форум » [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

Dima: Andrey пишет: Жду от Димы. Андрей выше чуть я давал пример как раз на несколько полей и BM фильтром.

Andrey: Dima пишет: Андрей выше чуть я давал пример как раз на несколько полей и BM фильтром. у меня в тесте, несколько сложнее поиск. (В базе 12 полей FAM1-FAM12, NAME1-NAME12, OTCH1-OTCH12) По одному полю делать понятно как. А как делать в моем случае ? Прими участие в составление теста !

Dima: Andrey пишет: По одному полю делать понятно как. Там чуть ниже есть пример для 3-х полей (то же самое сделать можно для 12 полей) ПС Ключевое слово в сообщении CrazyFilter


SergKis: Andrey пишет:dbEvalDirect() - не работает, слишком большое условие поиска (больше 900 символов) Зачем так мучить выражение, можно (разбив на части и поместив в массив) примерно так: [pre2] FUNC MyFltr( aBlock ) LOCAL i FOR i := 1 TO len(aBlock) IF ! EVal(aBlock[ i ] RETURN .F. ENDIF NEXT RETURN .T. [/pre2]

SergKis: Andrey После dbEvalDirect(), без BM фильтра можно положить, полученный массив в dbf MEMIO (если не проходит массив в browse, как писал Pasha) и на него browse, добавив в skipper перемещение на нужную RecNo в основном алисе (как писал Dima) и ВСЕ старое должно заработать.

Andrey: SergKis Спасибо БОЛЬШОЕ !

Pasha: Andrey пишет: dbEvalDirect() - не работает, слишком большое условие поиска (больше 900 символов). Павел, если можно сделать большую строку поиска, то очень хорошо, если нет, то не страшно. Что еще за строка символов ? 2-й параметр - это блок кода, который должен вернуть логическое значение. Точно так же, как и в dbEval().

Dima: Pasha Он не пытается понять что пишут мне кажется.............подавай готовый код и тд и тп , возможно устал

Andrey: Pasha пишет: Что еще за строка символов ? 2-й параметр - это блок кода, который должен вернуть логическое значение. Точно так же, как и в dbEval(). Pasha пишет выше: func main Local bFor := {|| "Охрана"$Otkomu} Local bDo := {|| nk ++} ........ dbEvalDirect(bDo, bFor) .......... В моем случае: cFilterTo:= '("ИВАНОВ"$UPPER(FAM1).AND."ИВАН"$UPPER(NAME1).AND."ИВАНОВИЧ"$UPPER(OTCH1)).OR.("ИВАНОВ"$UPPER(FAM2).... ' - и т.д. Длина строки поиска=956 символов FUNCTION MyFindEvalDirect(cFilterTo,cFio) Local bDo := {|| AADD(aRecs, RECNO()) } Local bFilter := {|| cFilterTo } ...... dbEvalDirect(bDo, bFilter) Может я неправильно описал ? Извиняюсь за своё косноязычее....

Andrey: Dima пишет: Он не пытается понять что пишут мне кажется.............подавай готовый код и тд и тп , возможно устал Да пытаюсь понять и сделать применительно к своей задаче. Из-за этого и отдельный пример сделал. Устал, это точно. Разбираюсь с твоим CrazyFilter.... У меня в тесте - LOCATE отрабатывает за 0,01 сек. (удивительно быстро..., всегда считал его медленным) Там код вот такой есть: DBSETORDER(0) LOCATE FOR &cFilterTo DO WHILE Found() // help xHarbour Language Reference Guide nRec++ AADD( aRecno, RECNO() ) // записать в массив CONTINUE ENDDO Но почему то не получается как у тебя: RDDSETDEFAULT( "BMDBFCDX" ) SELECT DOGOVOR BM_DBSETFILTERARRAY(aRecno) BROWSE()

Dima: Andrey пишет: Но почему то не получается как у тебя: RDDSETDEFAULT( "BMDBFCDX" ) Harbour ?

Andrey: Dima пишет: Harbour ? хХарбор 1.2.3 Пример здесь - выше. Исправленных файл из проекта. http://files.mail.ru/0FB87DC650FA43E0969EB86D82B447A0

Dima: Andrey Вот живой пример (фильтр по 3-м полям) для HARBOUR (В Иксах не проверял так как их нет у меня) [pre2] #include "dbinfo.ch" REQUEST BMDBFNTX Proc main() FIELD F1,F2,F3 RDDSETDEFAULT( "BMDBFNTX" ) cls aeval(directory("tst.*"),{|x|ferase(x[1])}) dbCreate("tst", {{"F1", "C", 20, 0},{"F2", "c", 20, 0},{"F3", "c", 20, 0}}) USE tst while lastrec()<100000 dbappend() F1:= strzero(recno(),10)+chr(recno()%26+asc("A")) F2:= strzero(recno(),10)+chr(recno()%26+asc("B")) F3:= strzero(recno(),10)+chr(recno()%26+asc("C")) enddo INDEX ON F1 TO tst1 INDEX ON F2 TO tst2 additive INDEX ON F3 TO tst3 additive if CrazyFilter({{1,".*101.*A"},{2,".*101.*B"},{3,".*101.*C"}}) browse() else ? "Нет подходящих записей" endif return ****************** Func CrazyFilter(par) local i local amas local cRegex local ret:=.t. for i=1 to len(par) amas:={} if !empty(par[ i ][2]) cRegex:=par[ i ][2] cRegex:=HB_REGEXCOMP(cRegex) dbsetorder(par[ i ][1]) dbgotop() while !eof() if HB_REGEXHAS(cRegEx,ordkeyval()) aadd(amas,recno()) endif dborderinfo(DBOI_SKIPREGEX,,,cRegex) enddo BM_DBSETFILTERARRAY(amas) endif next if len(amas)==0 dbclearfilter() ret:=.f. endif return ret [/pre2] Сборка c:\hb32\bin\hbmk2 t -lrddbm

Sergy: Andrey пишет: В моем случае: cFilterTo:= '("ИВАНОВ"$UPPER(FAM1).AND."ИВАН"$UPPER(NAME1).AND."ИВАНОВИЧ"$UPPER(OTCH1)).OR.("ИВАНОВ"$UPPER(FAM2).... ' - и т.д. Не очень понимаю, какая задача может потребовать столь чудовищной структуры данных - 12 ФИО в одной записи... Точно от этого никак не избавиться ?

Andrey: Sergy пишет: Точно от этого никак не избавиться ? Было так заведено... году так в 1998 ... Тогда мемо поля пропадали и не было Харбора, а только Клипер. Пришлось извратиться... Sergy пишет: какая задача может потребовать столь чудовищной структуры данных - 12 ФИО в одной записи... Приватизация жилья. 12 полей Фамилий + 12 полей Имен + 12 полей Отчеств на ОДНУ запись ! Живет и работает программа со свистом... : Стоит ли переделывать рабочую программу, если поиск сейчас занимает 01 сек ?

Andrey: Тесты на сетке: Путь к БД: \\SERVERXP\DBF-Search\DBF\ Инфо о БД: Кол-во записей в БД = 101000 OS: Windows 8 Professional 6.02.9200 ВСЕГО ЗАТРАЧЕНО на поиск (SET FILTER) = 01 мин. 47 сек. ВСЕГО ЗАТРАЧЕНО на поиск (Locate) = 29 сек. Многовато будет....

Andrey: Dima пишет: Вот живой пример (фильтр по 3-м полям) для HARBOUR (В Иксах не проверял так как их нет у меня) Нет в хХарборе HB_REGEXHAS Чем можно заменить ?

Dima: Andrey пишет: Нет в хХарборе HB_REGEXHAS Чем можно заменить ? http://clipper.borda.ru/?1-4-0-00000939-000-0-0-1383983897

Sergy: Как вариант - сделать примерно так: [pre2]FUNC MyFilter(cName1,cName2,cName3) LOCAL i,result result := FALSE FOR i:=1 TO 12 IF cName1 $ &("FAM"+NTRIM(i)) .AND. ; cName2 $ &("NAME"+NTRIM(i)) .AND. ; cName3 $ &("OTCH"+NTRIM(i)) result := TRUE EXIT NEXT i RETURN result [/pre2] А если нехота использовать макросы, можно получить доступ через FIELDPOS() - если записей очень много, есть подозрение что он будет работать быстрее. Только нужно определиться, в каком порядке идут имена, отчества и фамилии. И в последствии постараться не менять структуру. А потом уже фильтровать на основании блока кода {|| MyFilter("ИВАНОВ","ИВАН","ИВАНОВИЧ")} Дай погудю немного: Andrey пишет: Приватизация жилья. 12 полей Фамилий + 12 полей Имен + 12 полей Отчеств на ОДНУ запись ! Живет и работает программа со свистом... : Стоит ли переделывать рабочую программу, если поиск сейчас занимает 01 сек ? Ниче не нужно переделывать. Все наши проги работают лучше всех, несмотря на классический пример неправильной структуры данных. В одной приватизации может участвовать два человека, в другой - три, а десяток пустых ФИО (т.е. примерно тридцать полей) будут болтаться в записи... А если нужно будет приватизировать одну жилплощадь на 13 человек - одного придется обломать... А потом возникают "спасите-помогите, строка поиска занимает килобайт и препроцессор/макрокомпилер не может ее проглотить..."

Pasha: Andrey пишет: В моем случае: cFilterTo:= '("ИВАНОВ"$UPPER(FAM1).AND."ИВАН"$UPPER(NAME1).AND."ИВАНОВИЧ"$UPPER(OTCH1)).OR.("ИВАНОВ"$UPPER(FAM2).... ' - и т.д. Длина строки поиска=956 символов FUNCTION MyFindEvalDirect(cFilterTo,cFio) Local bDo := {|| AADD(aRecs, RECNO()) } Local bFilter := {|| cFilterTo } ...... dbEvalDirect(bDo, bFilter) Может я неправильно описал ? Извиняюсь за своё косноязычее.... Не буду даже пытаться объяснить, что к чему, просто покажу, как надо сделать: // Создание блока кода из выражения поиска bFilter := &("{||" + cFilterTo + "}") ...... dbEvalDirect(bDo, bFilter)



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