Автор | Сообщение |
|
| |
Пост N: 368
Зарегистрирован: 08.07.06
|
|
Отправлено: 08.11.13 13:41. Заголовок: DBFNTX: как грамотно прервать работу SET FILTER ?
Добрый день Есть некая таблица: date D8 idx1 N5 idx2 N5 flags C5 string C80 И есть фрагмент программы: filter_sx := "" SELE table SET FILT TO IIF(LEN(filter_sx)==0,TRUE,(filter_sx $ table->str)) DBEDIT(...) Т.е. пока в строку фильтра ничего не попало - видны все записи. Если юзер хочет отфильтровать часть записей по содержимому строки - срабатывает фильтр. Возникает ситуация: если таблица большая (>100 тыс записей), расположена на сервере и юзер желает увидеть какие-то "редкие" записи, процесс фильтрации начинает занимать непозволительно долгое время. Юзер понимает, что лучше набрать в фильтре что-то другое, но как остановить процесс текущей фильтрации ? Делал так: 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 Но данный метод приводит вообще к чудным результатам: в 90% случаев после нажатия Esc - DBEDIT() начинает бешено прокручивать список вверх и зависает на первом элементе таблицы. Насколько понял по отладчику, что-то непонятное (для меня) происходит в недрах объекта в районе :Stabilize() С какой стороны к этому вопросу подступиться? PS: в Clipper было тоже самое - но когда тормозило всё, это было не так заметно, а сейчас, на фоне быстрой и адекватной работы Harbour... напрягает....
|
|
|
Ответов - 120
, стр:
1
2
3
4
5
6
All
[только новые]
|
|
|
| |
Пост N: 3706
Зарегистрирован: 17.05.05
|
|
Отправлено: 10.11.13 15:20. Заголовок: Sergy пишет: ProcSt..
Sergy пишет: Самопал или либа нужна какая ? Вероятно самопал что то типа FUNC ProcStack() LOCAL n := 0 Local ret:="" WHILE ! Empty( ProcName( n ) ) ret+=" "+ProcName( n++ ) ENDDO RETURN ret
|
|
|
|
| постоянный участник
|
Пост N: 3075
Зарегистрирован: 12.09.06
|
|
Отправлено: 10.11.13 15:47. Заголовок: Dima Есть в базе по..
Dima Есть в базе поля FIO1, FIO2, FIO3 .... FIO12 (C 45) А индексы нужно делать тоже сразу по каждому полю ? И как сделать поиск сразу по всем полям за один проход по базе ? Набросай пожалуйста заготовку, как правильно это организовать.
|
|
|
|
| |
Пост N: 3707
Зарегистрирован: 17.05.05
|
|
Отправлено: 10.11.13 15:59. Заголовок: Заготовку не накидаю..
Заготовку не накидаю а вот идея как бы есть (развивай) Да держим по каждому полю индекс что бы быстро отобрать нужные записи (но это дело вкуса) Переключаемся на первый поисковый индекс и ищем как я выше описывал в примере. Отобрали подходящие записи в массив и установили BM фильтр. Переключаемся на второй поисковый индекс и повторяем процедуру поиска по этому полю собрав подходящие записи в массив. Снимаем BM фильтр (вероятно можно и не снимать) и ставим новый и так далее. Теоретически должно работать быстро. Чем дальше тем меньше записей будет попадать в обработку. Пример #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 Вот такую функцию можно сделать //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
|
|
|
|
| |
Пост N: 136
Зарегистрирован: 19.05.05
|
|
Отправлено: 10.11.13 16:54. Заголовок: Для этого лучше орга..
Для этого лучше организовать поиск по другому, ИМХО конечно. Заводится дополнительная база из двух полей FIO C(45) NREC N(7) (к примеру, если записей в базе < 9999999 ) в базе индекс по FIO и NREC . Тогда не надо выполнять поиск по всем 12 индексам. Выполнив один поиск по СЛОВУ получаем номера всех записей, у которых встречаются заданное слово в любом из полей FIO1 - FIO12. Если необходимо выполнить поиск по нескольким словам придется, естественно, выполнить пересечение. Усложнится ведение, так как при удалении записи из основной базы необходимо удалить все записи из дополнительной базы, при выполнении PACK перестроить полностью всю базу, но такие процедуры как правило выполняются ночью или в выходные и это по времени не критично. Но поиск будет быстрее. Это-же самое можно применить и для Sergy. Каждре слово из string писать отдельной записью. Естественно, будут ограничения, если есть слово АБВГД поиск можно выполнять по условию АБВ*, но не *БВГ*
|
|
|
|
| |
Пост N: 376
Зарегистрирован: 08.07.06
|
|
Отправлено: 10.11.13 17:54. Заголовок: Dima пишет: Самопал..
Dima пишет: цитата: | Самопал или либа нужна какая ? |
| * ---------------------------------------- * 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 * ---------------------------------------- * Удобно в некоторых случаях делать например так: ProcStack(,,TRUE,CRLF) или просто ProcStack()
|
|
|
|
| постоянный участник
|
Пост N: 126
Зарегистрирован: 29.05.10
|
|
Отправлено: 11.11.13 13:20. Заголовок: Sergy пишет: Один ю..
Sergy пишет: цитата: | Один юзер хочет прямо сейчас найти "Моск", юзер на соседнем компе - "Якут" в тоже самое время. Через минуту первому потребовалось "Санкт-П", а другому - "Казань". Через пару минут дальше - "Иваново". Третий юзер в тоже самое время начал искать "Владивосток". Перестраивать индекс каждый раз? |
| Нет. Меняется временный индекс да и тот у каждого юзера свой ( на локальной машине )
|
|
|
|
| постоянный участник
|
Пост N: 3076
Зарегистрирован: 12.09.06
|
|
Отправлено: 11.11.13 15:47. Заголовок: ММК пишет: Нет. Мен..
ММК пишет: цитата: | Нет. Меняется временный индекс да и тот у каждого юзера свой ( на локальной машине ) |
| Ага. Я это имел в виду.
|
|
|
|
| |
Пост N: 3708
Зарегистрирован: 17.05.05
|
|
Отправлено: 11.11.13 16:16. Заголовок: Andrey Так хотел пр..
Andrey Так хотел пример (который я дал) и ни чего не ответил в плане BM фильтра.........или не юзал ?
|
|
|
|
| постоянный участник
|
Пост N: 3077
Зарегистрирован: 12.09.06
|
|
Отправлено: 11.11.13 19:40. Заголовок: Dima пишет: или не ..
Dima пишет: Спасибо БОЛЬШОЕ. Я его делаю, но там всего много навороченного, так что делаю отдельную версию.
|
|
|
|
| |
Пост N: 377
Зарегистрирован: 08.07.06
|
|
Отправлено: 17.11.13 19:04. Заголовок: ММК пишет: Нет. Мен..
ММК пишет: цитата: | Нет. Меняется временный индекс да и тот у каждого юзера свой ( на локальной машине ) |
| Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно).
|
|
|
|
| постоянный участник
|
Пост N: 3094
Зарегистрирован: 12.09.06
|
|
Отправлено: 17.11.13 20:01. Заголовок: Sergy пишет: Ну вед..
Sergy пишет: цитата: | Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно). |
| Поверь, это происходить ОЧЕНЬ быстро. База примерно 50.000 записей, поиск по различным полям базы, включая текстовые поля и мемо-поля. Индексирование происходит за 40-60 сек. сек. Если есть уже открытый индекс по которому нужно выбрать значение, т.е. допустим ФИО и дату (любую) и еще чего нибудь, то 10-20 сек. Эту технологию сделал еще в 1997 г. на Клипере 5.3 и до сих пор использую. Хотя надо бы переходить на LetoDB....
|
|
|
|
|
| |
Пост N: 68
Зарегистрирован: 17.10.05
|
|
Отправлено: 18.11.13 13:33. Заголовок: Andrey пишет: Ну ве..
Andrey пишет: цитата: | Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно). |
| когда-то, когда Clipper еще помещался на 720к дискету, существовала такая приблуда, которая называлась SUBINDEX. Это была и автономная программка, и библиотека для S'87 и 5.01.. по памяти где-то так: subindex(исходный-ntx,результирующий-ntx,маска-отбора) т.е. строился индекс на основании другого индекса, даже не обращаясь к записям таблицы. маска например "ИВАНОВ*" я просто для иллюстрации подхода ;-)
|
|
|
|
| moderator
|
Пост N: 718
Зарегистрирован: 06.07.06
|
|
Отправлено: 18.11.13 14:43. Заголовок: LYSK пишет: существ..
LYSK пишет: цитата: | существовала такая приблуда, которая называлась SUBINDEX. |
| Это была одна из фичей Six3. В Harbour это тоже есть, сам, правда, не пробовал.
|
|
|
|
| Администратор
|
Пост N: 3040
Зарегистрирован: 23.05.05
|
|
Отправлено: 18.11.13 15:07. Заголовок: Andrey пишет: Повер..
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*) Какие есть еще подводные камни - надо подумать, так как я эту функцию сделал спонтанно, как только появилась такая идея.
|
|
|
|
| |
Пост N: 3720
Зарегистрирован: 17.05.05
|
|
Отправлено: 18.11.13 15:20. Заголовок: Pasha пишет: dbEval..
Pasha пишет: цитата: | dbEvalDirect(bDo, bFilter) |
| Вариант конечно когда нет подходящего индекса а если есть то быстрее будет конечно через dborderinfo(DBOI_SKIPREGEX,,,cRegex) сделать выборку. PS Примерчик выше
|
|
|
|
| |
Пост N: 378
Зарегистрирован: 08.07.06
|
|
Отправлено: 18.11.13 15:27. Заголовок: Pasha пишет: Исполь..
Pasha пишет: цитата: | Использование индекса с условием for - это некрасивое решение, поскольку для поиска формируется ненужный разовый индекс, при создании которого идет выборка всего файла. |
| Согласен с этим. Поскольку при таком подходе из всех возможных вариантов будет использован наиболее медленный, с перечитыванием всего файла с начала и до конца. Банальный SET FILTER так будет себя вести только в том случае, если удовлетворяющих записей меньше, чем строк DBEDIT() на экране. Во всех остальных случаях он будет быстрее. цитата: | Вот эта маленькая функция: http://zalil.ru/34818847 Использование функции: dbEvalDirect(bDo, bFilter) Смысл параметров такой же, как и в dbEval() |
| Спасибо, изучу.
|
|
|
|
| постоянный участник
|
Пост N: 128
Зарегистрирован: 29.05.10
|
|
Отправлено: 18.11.13 15:30. Заголовок: LYSK пишет: Ну ведь..
LYSK пишет: цитата: | Ну ведь для того, чтобы перестроить индекс (даже локально) - нужно перечитать все записи, хранящиеся на сервере (удаленно). |
| LYSK пишет: цитата: | строился индекс на основании другого индекса, даже не обращаясь к записям таблицы. маска например "ИВАНОВ*" я просто для иллюстрации подхода ;-) |
| Т.е. необходимо и достаточно наличие хотя бы одного индекса, на основе которого и происходят все последующие выборки. Вот, например , сырье .Индекс по наименованию. Выбираем ( правой кл. мыши ) поле "остаток на нач.дня" равное нулю ( или вводим любое нужное значение )
|
|
|
|
| Администратор
|
Пост N: 3041
Зарегистрирован: 23.05.05
|
|
Отправлено: 18.11.13 15:31. Заголовок: Sergy пишет: Спас..
Sergy пишет: Эта функция не для прерывания фильтра, я просто не стал создавать новую тему, раз уж вопрос с условной индексацией всплыл здесь опять. Назначение функции - быстрый поиск по всему файлу с произвольным условием. Можно еще добавить параметр - прерывание поиска при первом успешном попадании.
|
|
|
|
| постоянный участник
|
Пост N: 129
Зарегистрирован: 29.05.10
|
|
Отправлено: 18.11.13 15:35. Заголовок: В этой выборке меня ..
В этой выборке меня интересует сырье , для которого были поступления- соответственно другое поле и другое значение ( выборка в выборке ) Это можно делать в любой последовательности для любого кол-вы полей
|
|
|
|
| Администратор
|
Пост N: 3042
Зарегистрирован: 23.05.05
|
|
Отправлено: 18.11.13 17:05. Заголовок: Pasha пишет: Какие ..
Pasha пишет: цитата: | Какие есть еще подводные камни - надо подумать, |
| Я вижу только один такой: если в процессе чтения файла через dbEvalDirect другой пользователь добавит запись, то dbEvalDirect эту запись не увидит, так как RecCount определяется перед началом выборки. Но это и есть причина такой высокой скорости: при обычной выборке каждый раз обновляется RecCount, а для этого делается fseek на конец файла, что заметно замедляет работу. Но и индексация происходит точно так же: если во время индексации другой пользователь добавит новую запись, эта запись не попадет в индекс.
|
|
|
Ответов - 120
, стр:
1
2
3
4
5
6
All
[только новые]
|
|