Форум » [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: Sergy пишет: ProcStack() Самопал или либа нужна какая ? Вероятно самопал что то типа [pre2] FUNC ProcStack() LOCAL n := 0 Local ret:="" WHILE ! Empty( ProcName( n ) ) ret+=" "+ProcName( n++ ) ENDDO RETURN ret [/pre2]

Andrey: Dima Есть в базе поля FIO1, FIO2, FIO3 .... FIO12 (C 45) А индексы нужно делать тоже сразу по каждому полю ? И как сделать поиск сразу по всем полям за один проход по базе ? Набросай пожалуйста заготовку, как правильно это организовать.

Dima: Заготовку не накидаю а вот идея как бы есть (развивай) Да держим по каждому полю индекс что бы быстро отобрать нужные записи (но это дело вкуса) Переключаемся на первый поисковый индекс и ищем как я выше описывал в примере. Отобрали подходящие записи в массив и установили BM фильтр. Переключаемся на второй поисковый индекс и повторяем процедуру поиска по этому полю собрав подходящие записи в массив. Снимаем BM фильтр (вероятно можно и не снимать) и ставим новый и так далее. Теоретически должно работать быстро. Чем дальше тем меньше записей будет попадать в обработку. Пример [pre2] #include "dbinfo.ch" REQUEST BMDBFNTX Proc main() FIELD F1,F2,F3 local cPattern,cRegex local amas:={} local nsec 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 nsec:=seconds() cRegex:=".*101.*A" cRegex:=HB_REGEXCOMP(cRegex) dbsetorder(1) dbgotop() while !eof() if HB_REGEXHAS(cRegEx,ordkeyval()) aadd(amas,recno()) endif dborderinfo(DBOI_SKIPREGEX,,,cRegex) enddo BM_DBSETFILTERARRAY(amas) amas:={} dbsetorder(2) dbgotop() cRegex:=".*510.*B" cRegex:=HB_REGEXCOMP(cRegex) dbgotop() while !eof() if HB_REGEXHAS(cRegEx,ordkeyval()) aadd(amas,recno()) endif dborderinfo(DBOI_SKIPREGEX,,,cRegex) enddo BM_DBSETFILTERARRAY(amas) amas:={} dbsetorder(3) dbgotop() cRegex:=".*510.*C" cRegex:=HB_REGEXCOMP(cRegex) dbgotop() while !eof() if HB_REGEXHAS(cRegEx,ordkeyval()) aadd(amas,recno()) endif dborderinfo(DBOI_SKIPREGEX,,,cRegex) enddo BM_DBSETFILTERARRAY(amas) ? seconds()-nsec // 0.16 сек wait browse() return [/pre2] Вот такую функцию можно сделать [pre2] //CrazyFilter({{1,".*101.*A"},{2,".*101.*B"},{3,".*101.*C"}}) Func CrazyFilter(par) local i local amas local cRegex 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 return nil [/pre2]


nick_mi: Для этого лучше организовать поиск по другому, ИМХО конечно. Заводится дополнительная база из двух полей FIO C(45) NREC N(7) (к примеру, если записей в базе < 9999999 ) в базе индекс по FIO и NREC . Тогда не надо выполнять поиск по всем 12 индексам. Выполнив один поиск по СЛОВУ получаем номера всех записей, у которых встречаются заданное слово в любом из полей FIO1 - FIO12. Если необходимо выполнить поиск по нескольким словам придется, естественно, выполнить пересечение. Усложнится ведение, так как при удалении записи из основной базы необходимо удалить все записи из дополнительной базы, при выполнении PACK перестроить полностью всю базу, но такие процедуры как правило выполняются ночью или в выходные и это по времени не критично. Но поиск будет быстрее. Это-же самое можно применить и для Sergy. Каждре слово из string писать отдельной записью. Естественно, будут ограничения, если есть слово АБВГД поиск можно выполнять по условию АБВ*, но не *БВГ*

Sergy: Dima пишет: Самопал или либа нужна какая ? [pre2] * ---------------------------------------- * FUNC ProcStack(nFrom,nTo,lNeedLines,cDefSep) LOCAL i,res,sx,j DEFAULT nFrom TO 1 DEFAULT nTo TO 40 DEFAULT lNeedLines TO FALSE DEFAULT cDefSep TO ":" res:="" FOR i:=nFrom TO nTo sx:=PROCNAME(i) IF !EMPTY(sx) res += sx IF lNeedLines res += "("+NTRIM(PROCLINE(i))+")" ENDIF IF i < nTo res += cDefSep ENDIF ENDIF NEXT i RETURN res * ---------------------------------------- *[/pre2] Удобно в некоторых случаях делать например так: ProcStack(,,TRUE,CRLF) или просто ProcStack()

ММК: Sergy пишет: Один юзер хочет прямо сейчас найти "Моск", юзер на соседнем компе - "Якут" в тоже самое время. Через минуту первому потребовалось "Санкт-П", а другому - "Казань". Через пару минут дальше - "Иваново". Третий юзер в тоже самое время начал искать "Владивосток". Перестраивать индекс каждый раз? Нет. Меняется временный индекс да и тот у каждого юзера свой ( на локальной машине )

Andrey: ММК пишет: Нет. Меняется временный индекс да и тот у каждого юзера свой ( на локальной машине ) Ага. Я это имел в виду.

Dima: Andrey Так хотел пример (который я дал) и ни чего не ответил в плане BM фильтра.........или не юзал ?

Andrey: Dima пишет: или не юзал ? Спасибо БОЛЬШОЕ. Я его делаю, но там всего много навороченного, так что делаю отдельную версию.

Sergy: ММК пишет: Нет. Меняется временный индекс да и тот у каждого юзера свой ( на локальной машине ) Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно).

Andrey: Sergy пишет: Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно). Поверь, это происходить ОЧЕНЬ быстро. База примерно 50.000 записей, поиск по различным полям базы, включая текстовые поля и мемо-поля. Индексирование происходит за 40-60 сек. сек. Если есть уже открытый индекс по которому нужно выбрать значение, т.е. допустим ФИО и дату (любую) и еще чего нибудь, то 10-20 сек. Эту технологию сделал еще в 1997 г. на Клипере 5.3 и до сих пор использую. Хотя надо бы переходить на LetoDB....

LYSK: Andrey пишет: Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно). когда-то, когда Clipper еще помещался на 720к дискету, существовала такая приблуда, которая называлась SUBINDEX. Это была и автономная программка, и библиотека для S'87 и 5.01.. по памяти где-то так: subindex(исходный-ntx,результирующий-ntx,маска-отбора) т.е. строился индекс на основании другого индекса, даже не обращаясь к записям таблицы. маска например "ИВАНОВ*" я просто для иллюстрации подхода ;-)

alkresin: LYSK пишет: существовала такая приблуда, которая называлась SUBINDEX. Это была одна из фичей Six3. В Harbour это тоже есть, сам, правда, не пробовал.

Pasha: Andrey пишет: Поверь, это происходит ОЧЕНЬ быстро. База примерно 50.000 записей, поиск по различным полям базы, включая текстовые поля и мемо-поля. Индексирование происходит за 40-60 сек. сек. Если есть уже открытый индекс по которому нужно выбрать значение, т.е. допустим ФИО и дату (любую) и еще чего нибудь, то 10-20 сек. Эту технологию сделал еще в 1997 г. на Клипере 5.3 и до сих пор использую. Использование индекса с условием for - это некрасивое решение, поскольку для поиска формируется ненужный разовый индекс, при создании которого идет выборка всего файла. Другое дело - индекс действительно строится очень быстро, даже быстрее цикла по всему файлу без индекса. Причина этого - данные из файла считываются в режиме _SET_STRICTREAD, непосредственно из файла с использованием служебного буфера. Сразу же возникает мысль использовать такой же режим для поиска. Вот эта маленькая функция: http://zalil.ru/34818847 Использование функции: dbEvalDirect(bDo, bFilter) Смысл параметров такой же, как и в dbEval() Пример: aRecs := {} dbEvalDirect({|| AADD(aRecs, RecNo())}, bFilter) Функция не учитывает установки set filter, scope, и прочее, считывает полностью весь файл, от первой до последней записи. Работает еще быстрее, чем построение индекса с for, так как не создается ненужный индекс. Использовать ее можно только для rdd DBF*: DBFNTX,DBFCDX и их производных (BM*) Какие есть еще подводные камни - надо подумать, так как я эту функцию сделал спонтанно, как только появилась такая идея.

Dima: Pasha пишет: dbEvalDirect(bDo, bFilter) Вариант конечно когда нет подходящего индекса а если есть то быстрее будет конечно через dborderinfo(DBOI_SKIPREGEX,,,cRegex) сделать выборку. PS Примерчик выше

Sergy: Pasha пишет: Использование индекса с условием for - это некрасивое решение, поскольку для поиска формируется ненужный разовый индекс, при создании которого идет выборка всего файла. Согласен с этим. Поскольку при таком подходе из всех возможных вариантов будет использован наиболее медленный, с перечитыванием всего файла с начала и до конца. Банальный SET FILTER так будет себя вести только в том случае, если удовлетворяющих записей меньше, чем строк DBEDIT() на экране. Во всех остальных случаях он будет быстрее. Вот эта маленькая функция: http://zalil.ru/34818847 Использование функции: dbEvalDirect(bDo, bFilter) Смысл параметров такой же, как и в dbEval() Спасибо, изучу.

ММК: LYSK пишет: Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно). LYSK пишет: строился индекс на основании другого индекса, даже не обращаясь к записям таблицы. маска например "ИВАНОВ*" я просто для иллюстрации подхода ;-) Т.е. необходимо и достаточно наличие хотя бы одного индекса, на основе которого и происходят все последующие выборки. Вот, например , сырье .Индекс по наименованию. Выбираем ( правой кл. мыши ) поле "остаток на нач.дня" равное нулю ( или вводим любое нужное значение )

Pasha: Sergy пишет: Спасибо, изучу. Эта функция не для прерывания фильтра, я просто не стал создавать новую тему, раз уж вопрос с условной индексацией всплыл здесь опять. Назначение функции - быстрый поиск по всему файлу с произвольным условием. Можно еще добавить параметр - прерывание поиска при первом успешном попадании.

ММК: В этой выборке меня интересует сырье , для которого были поступления- соответственно другое поле и другое значение ( выборка в выборке ) Это можно делать в любой последовательности для любого кол-вы полей

Pasha: Pasha пишет: Какие есть еще подводные камни - надо подумать, Я вижу только один такой: если в процессе чтения файла через dbEvalDirect другой пользователь добавит запись, то dbEvalDirect эту запись не увидит, так как RecCount определяется перед началом выборки. Но это и есть причина такой высокой скорости: при обычной выборке каждый раз обновляется RecCount, а для этого делается fseek на конец файла, что заметно замедляет работу. Но и индексация происходит точно так же: если во время индексации другой пользователь добавит новую запись, эта запись не попадет в индекс.



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