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

Andrey: Какое решение самое лучшее для БЫСТРОГО поиска по различным полям без открытых индексов ? Решение Pasha или Dima пишет: Вариант конечно когда нет подходящего индекса а если есть то быстрее будет конечно через dborderinfo(DBOI_SKIPREGEX,,,cRegex)

Dima: Andrey Берешь базу на пару-тройку лимонов записей и делаешь 2 теста и смотришь что быстрее. PS Ответь мне на ЛС

Andrey: Dima пишет: Берешь базу на пару-тройку лимонов записей и делаешь 2 теста и смотришь что быстрее. На ЛС ответил. Помоги сделать эти тесты ? Ну очень интересно... Всем тоже узнать хочется !!! А я что нибудь другое сваяю для тебя ?


Dima: Andrey Давай мне базу да побольше и условия фильтрации (для начала хватит по одному-двум полям) или Опиши структуру базы , чем заполнять и что будем искать.

Andrey: Хорошо, делаю.

ММК: Andrey пишет: Какое решение самое лучшее для БЫСТРОГО поиска по различным полям без открытых индексов ? Решение Pasha или Dima пишет: Если воообще без индексов , то Locate :) Но и тут надо уточнить- "дикий" поиск или точный. Будет ли продолжение ( добавление следующих условий ) или "начни с начала" :))) Каждая задача имеет свое конкретное решение и сравнивать их нет смысла

Pasha: Andrey пишет: Хорошо, делаю. Тест Обьект тестирования - doma.dbf из КЛАДР, размер - 203М, более 2-х млн. записей Выражение поиска: "229"$Name Количество попаданий - 2310 Файл открыт через DBFCDX с индексом в режиме shared Тест1: поиск без управляющего индекса - 5.9 сек Тест2: поиск c управляющим индексом - 18.6 сек Тест3: построение индекса с for - 0.83 сек Тест4: поиск dbEvalDirect - 0.81 сек Вывод (он и так напрашивался): - dbEvalDirect немного быстрее индексации с for Понятно, что с файлами таких экстремальных размеров редко имеешь дело. На файле размером 40M (~300 тыс.записей) время выполнения index ... for и dbEvalDirect примерно одинаково (просто погрешность выше, чем разница в скорости) На небольшом файле размером 8М (~40 тыс.записей) index ... for выполняется за 0.05 сек, а dbEvalDirect - за 0.03 сек. (разница чувствуется)

Andrey: Pasha БОЛЬШОЕ спасибо за тесты. Я свою заготовку переслал Диме. У меня там замороченей тест получается... В смысле много полей ищется сразу в базе. Можешь ли выложить свой исходник теста ? Хочется посмотреть код профессионала. Заранее спасибо.

Pasha: Такая большая проблема сделать простейший тест, что ли ? Вот код: [pre]Field TarCV, LS Field Otkomu, Date Field Name func main Local bFor := {|| "229"$Name} //Local bFor := {|| "Охрана"$Otkomu} //Local bFor := {|| "229"$TarCV} local cf := 'doma' //local cf := 'carecs' //local cf := 'tplus' Local nSec, nk Local bDo := {|| nk ++} request dbfcdx dbUseArea(.t., 'DBFCDX', cf,, .t.) ordListAdd(cf) dbSetOrder(0) nk := 0 nSec := Seconds() dbEval(bDo, bFor) ? 'DBFCDX-0', nk, Seconds() - nSec close dbUseArea(.t., 'DBFCDX', cf,, .t.) ordListAdd(cf) nk := 0 nSec := Seconds() dbEval(bDo, bFor) ? 'DBFCDX-1', nk, Seconds() - nSec go top nk := 0 nSec := Seconds() dbEvalDirect(bDo, bFor) ? 'DBFCDX-2', nk, Seconds() - nSec nSec := Seconds() index on Code tag (cf+'1') to (cf+'1') for "229"$Name //index on Date tag (cf+'1') to (cf+'1') for "Охрана"$Otkomu //index on LS tag (cf+'1') to (cf+'1') for "229"$TarCV ? 'Index for', ordKeyCount(), Seconds()-nSec close return nil[/pre]

Pasha: Можно классифицировать скорость выборки на 3 уровня: 1. При монопольном доступе к файлу, во время индексации, и в только что написанной функции dbEvalDirect Скорость максимальная, так как ресурсы расходуются только на чтение исходного файла, и больше ни на что. При этом для монопольного доступа возможно чтение любого фрагмента файла с управляющим индексом или без него. С индексом скорость будет немного хуже. Для индексации и dbEvalDirect считывается весь файл. Поскольку letodb открывает файлы монопольно, то на него не влияют факторы, описанные ниже. 2. Для файла, открытого в режиме shared без управляющего индекса. Скорость в несколько раз хуже за счет того, что при чтении каждой записи обновляется reccount. 3. Для файла, открытого в режиме shared с управляющим индексом. Скорость еще в несколько раз хуже, чем для п.2 Причина этого, кроме момента, указанного в п.2 - блокировка индекса на чтение при чтении каждой записи. Этот момент можно резко улучшить, задав dbOrderInfo(DBOI_READLOCK,,, .t.) перед выборкой. Еще причина - выборка записи по ключу в самом индексе, т.е. чтение индекса.

Andrey: А как отобразить отобранные записи в BROWSE() ? Я сталкивался с такой задачей, т.е. есть в массиве номера записей (полученные из поиска) которые нужно отобразить на экране. Как в таком случае поступают (с учетом работы в будущем не LetoDB) ?

Dima: Andrey BM фильтр (затем browse) я же пример приводил выше

Andrey: Dima пишет: BM фильтр (затем browse) я же пример приводил выше А без BM фильтра ? Я у себя давно делал поиск по подчиненной базе. Приходилось при поиске в доп.поле записывать 0, а при нахождении 1, а потом показывать записи с кодом=1 . Это не оптимально. Вот и хочу понять как такие вещи делать ?

Dima: Без BM будет не оптимально. Как вариант складывать можно выбранные записи во временную базу Или переделать Skipper что бы он ходил по массиву с номерами записей

Pasha: Andrey пишет: А как отобразить отобранные записи в BROWSE() ? Сделать источником данных для browse не р/о, а массив. В массив загонять те данные, которые надо выдать, плюс номер записи. После выбора нужной строки массива делать dbGoto() по номеру выбранной записи из строки массива.

Andrey: Pasha пишет: Сделать источником данных для browse не р/о, а массив. В массив загонять те данные, которые надо выдать, плюс номер записи. После выбора нужной строки массива делать dbGoto() по номеру выбранной записи из строки массива. А по другому никак ? Сложновато получается... Или мне кажется только ? Минимальный код для такого BROWSE() привести можете ?

Dima: Andrey Похоже читаешь сообщения но ни чего не тестишь отсюда и не понятки. Сделай простейший пример с BM фильтром и все станет ясно. Что касается Browse то он у каждого свой под свои задачи и в любом из них есть Skipper или аналог вот его и нужно переделать если что.

Andrey: Dima пишет: Похоже читаешь сообщения но ни чего не тестишь отсюда и не понятки. Делаю. На этой неделе выложу.

Sergy: Andrey пишет: А как отобразить отобранные записи в BROWSE() ? Я сталкивался с такой задачей, т.е. есть в массиве номера записей (полученные из поиска) которые нужно отобразить на экране. Как в таком случае поступают (с учетом работы в будущем не LetoDB) ? Со времен Clipper, когда нужных записей в большой таблице мало - выяснил, что гораздо эффективнее их считать в массив, а потом только их и показывать. Примерно так: [pre2] aRx:={} GO TOP DO WHILE !EOF() IF .... AADD(aRx,RECNO()) ENDIF SKIP ENDDO DbViewByArray(a+1,1,MAXROW()-5,MAXCOL()-1,aValues[1],"CallBackFunc",aRx,@nPos)[/pre2] где: [pre2] * ------------------------------------------------ * FUNC DbViewByArray(nTop,nLeft,nBottom,nRight,; cValues,cProcName,aRecNumbers,nPos) LOCAL i,bValues DEFAULT nPos TO 1 PRIV dbv_more:=TRUE,txt,rxn PRIV user_func:=cProcName IF EMPTY(cValues) .OR. EMPTY(cProcName) ShowAlert("DbViewByArray: No Parameters.") RETURN ELSEIF (VALTYPE(aRecNumbers) # "A") .OR. (LEN(aRecNumbers) = 0) ShowAlert("DbViewByArray: нет записей") RETURN ENDI bValues := {||&cValues} // блок кода для ускорения работы rxn := aRecNumbers // сразу копируем ссылку на массив DO WHILE dbv_more ////////////////////////////////////////////////// осн.цикл txt:=ARRAY(LEN(aRecNumbers)) FOR i:=1 TO LEN(aRecNumbers) GO aRecNumbers[ i ] txt[ i ] := EVAL(bValues) NEXT i @ nTop+LEN(txt),nLeft CLEAR TO nBottom,nRight // очистка конца экрана DbView2(0,nPos,save_pos) // redraw screen nPos:=ACHOICE(nTop,nLeft,nBottom,nRight,txt,,"DbView2",nPos,save_pos) ENDDO RETURN * ----------------------------- * FUNC DbView2(mode,index,scrn_pos) // call-back user func here! LOCAL user_result IF (index > LEN(txt)) .OR. (index < 1) index := 1 ENDI save_index := index save_pos := scrn_pos GO rxn[index] user_result := &user_func (TranslateMsg(mode),rxn,index) // вызов call-back функции пользователя save_rx := RECNO() DO CASE CASE user_result = DE_CONT; RETURN AC_CONT CASE user_result = DE_ABORT; dbv_more:=FALSE; RETURN AC_SELECT CASE user_result = DE_REFRESH; dbv_more:=TRUE; RETURN AC_SELECT CASE user_result = DBVIEW_DOWN; PutInKbd(CHR(K_DOWN)); RETURN AC_CONT CASE user_result = DBVIEW_UP; PutInKbd(CHR(K_UP)); RETURN AC_CONT OTHERWISE RETURN AC_CONT ENDC RETURN AC_CONT * ----------------------------- * [/pre2]

Andrey: Dima пишет: Похоже читаешь сообщения но ни чего не тестишь отсюда и не понятки. Сделал тест на базу 101тыс. записей. Реально рабочий пример. В базе 12 полей FAM1-FAM12, NAME1-NAME12, OTCH1-OTCH12 Делать поиск сразу по этим полям сложно. SET FILTER и LOCATE успешно ищет. Правда показ в BROWSE() по SET FILTER - тормоза ! Время поиска просто очень мало 1сек. Может у меня комп такой... На сети пока не тестил, на след.неделе сделаю. Поиск по SEEK не стал реализовывать. Что-то очень сложный алгоритм получается... dbEvalDirect() - не работает, слишком большое условие поиска (больше 900 символов). Павел, если можно сделать большую строку поиска, то очень хорошо, если нет, то не страшно. Dima пишет: пример с BM фильтром Жду от Димы. Вообще кто, что можете порекомендовать, пишите. Я не смотря на долгую работу с Клипером, всех тонкостей не знаю. Сам проект на хХарборе 1.2.3 http://files.mail.ru/5D8A880F51054F7BBB3A279FB1E279EA



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