Форум » [x]Harbour » Проблема с условной индексацией 2 » Ответить

Проблема с условной индексацией 2

Andrey: Возвращаюсь к этой теме повторно ! Раньше было получено рабочее решение: условной индексации по БД из 60 000 записей, имеющей 263 поля и занимающей 55 Мб решается так: nKolRecords := LASTREC() cIndexTo := "PADL(ALLTRIM(FIELD->CNumKvar),5)" cFilterTo := "NN==2010490.AND.!DELETED()" ---------------------------------------------------------------------------------- INDEX ON &cIndexTo TAG "ONE" TO ("temp.cdx") ; EVAL SAY_PROC() ; EVERY nKolRecords / 10 ; WHILE &cFilterTo ADDITIVE // замена на FOR &cFilterTo ADDITIVE увеличивает построение индекса на 6 сек. ORDSETFOCUS( "ONE" ) DBSETORDER(INDEXORD()) DBGOTOP() где SAY_PROC() - функция рисования бегунка, возвращает всегда .T. Сейчас потребовалась выборка по дате: cIndexTo := "DTOS(FIELD->DATEPRIX)+STR(FIELD->TIMEPRIX)" cFilterTo := "DATEVvod=CTOD("26.02.11).AND.KOPERAT=101.AND.!DELETED()" --------------------------------------------------------------------------------- INDEX ON &cIndexTo TAG "ONE" TO ("temp.cdx") ; EVAL SAY_PROC() ; EVERY nKolRecords / 10 ; WHILE &cFilterTo ADDITIVE ORDSETFOCUS( "ONE" ) DBSETORDER(INDEXORD()) DBGOTOP() Индекс строится мгновенно, но функция FOUND() всегда возвращает .F. - хотя если поставить заместо WHILE ---> FOR то нормально работает, правильно возвращает. но FOR - строит медленнее... чем WHILE ..... В чем дело, тип ДАТА или ЧИСЛА неправильно в фильтре ??? или еще чего ??? подскажите пожалуйста.

Ответов - 32, стр: 1 2 All

PSP: Выдержка из хэлпа: FOR <lForCondition> This is an optional logical expression which is evaluated for all records in the current work area. Those records where <lForCondition> yields .T. (true) are included in the index. The FOR condition is stored in the index file and is maintained by the database driver when records are updated. That is, if a record is changed so that it does not match the FOR condition, it is removed from the index. The FOR expression cannot exceed 250 characters in length. RDDs that do not support a FOR condition when creating indexes generate a runtime error when this option is used. WHILE <lWhileCondition> This is a logical expression indicating to continue index creation while the condition is true. The INDEX command stops evaluating records as soon as <lWhileCondition> yields .F. (false). Unlike the FOR expression, which is stored in the index file and exists throughout the lifetime of an index, the WHILE condition is only evaluated during index creation. It is discarded when the index is complete. Возможно, при использовании WHILE нужные записи просто не попадают в индекс.

Chikanuk: По-русски: FOR <lForCondition> <lForCondition> - необязательное логическое выражение, определяющее условие: FOR условие сохраняется в индексном файле и рассматривается в том случае, когда индексный файл восстанавливается, используя REINDEX. Значения индекса добавляются в индекс только для записей, когда <lForCondition> возвращает значение .T. (истина). WHILE <lWhileCondition> <lWhileCondition> - необязательное логическое выражение, определяющее условие: записи копируются в индекс, начинаясь от текущей записи до тех пор, пока <lWhileCondition> возвращает значение .T. (истина). При значении .F. (ложь) действие заканчивается. WHILE условие не сохраняется в индексном файле, рассматривается только в течение выполнения команды INDEX ON и не доступно в течении будущей реорганизации индекса с использованием REINDEX. Похоже, действительно, нужные записи просто не попадают в индекс. И "индекс строится мгновенно" потому, что при первом же .F. построение заканчивается или указатель был в конце таблицы. Попробуй на первую запись сперва переместиться: DBGOTOP() INDEX ON &cIndexTo TAG "ONE" TO ("temp.cdx") ; //...

Pasha: Да сам подход неправильный. Нельзя так строить индекс. Если для индекса задать условие for, то при создании индекса будут выбраны все записи в файле, но в индекс попадут только те, которые удовлетворяют for. То есть, для построения индекса нужна выборка всех записей файла. Если задать условие while, то при построении индекса будут выбраны записи до той, которая не удовлетворяет while. Поскольку физически записи могут располагаться произвольно, то, если первая запись не удовлетворяет этому условию, то в индекс не будет включено ни одной. Если вторая - то в индексе будет одна запись. Индекс будет построен мгновенно, но он будет пустой. Сама идея использовать временный индекс с for или while для разовой выборки порочна. Если использовать for, то для построения индекса необходимо выбрать все записи файла. Но ведь при использовании существующих индексов можно задать условие выборки только части записей. Использование же while просто бесполезно, правильность индекса зависит от случая. Практически всегда он будет неправильный Вывод: Андрей, откажись от этих глупостей, и используй традиционный подход.


Andrey: Chikanuk Спасибо большое за перевод и совет !!!

Andrey: PSP , я понял свою ошибку !!!

Andrey: Pasha пишет: Вывод: Андрей, откажись от этих глупостей, и используй традиционный подход. Спасибо большое за разъяснение. Я еще в 2008 году пытался про это понять. А как использовать традиционный подход ? Опиши технологию пожалуйста.... У меня сейчас строится условный индекс и отображается TBROWSE. Это быстро и выход за границы базы быстро пресекается, и реакция в самом TBROWSE быстрая. Без всяких кодоблоков. Эту технологию еще с Клипера 5.3 перетаскивал....

Pasha: Традиционный подход - это использование для выборок постоянных индексов со scope по ним. Дополнительно к scope можно использовать и условие/фильтр for. Надо определить, по каким полям могут быть чаще всего выборки, и соответственно заранее создать все тэги. Причем постоянный индекс может быть также с условием for. Но при этом индекс строится только один раз, и затем только обновляется

Chikanuk: Pasha: ОГРОМНОЕ спасибо за подробный и (имхо) правильный совет. Не пора ли FAQ завести на форуме? Andrey: спасибо за "спасибо" Andrey пишет: БД из 60 000 записей, имеющей 263 поля Широко развернулся

Andrey: Pasha пишет: Надо определить, по каким полям могут быть чаще всего выборки, и соответственно заранее создать все тэги. БД-Договоров. Список полей примерно 160. Номер договора, дата договора, адрес договора, филиал, заказчик и т.д. Пользователь может выбрать условие поиска по всему списку полей. Сделать заранее шаблоны поиска сложно. Так что здесь придется оставить условную индексацию с FOR. А на другие постоянные выборки буду делать по твоим рекомендациям. Спасибо большое Паша.

Pasha: Andrey пишет: Так что здесь придется оставить условную индексацию с FOR. Условная индексация с for означает, что: 1) необходим цикл по всем записям таблицы для создания индекса 2) затем - цикл по записям, которые присутствуют в созданном индексе для выбора записей, удовлетворяющих условию поиска Это менее оптимально, чем просто цикл по всей таблице без использования индекса. Оптимальный вариант: определить реквизиты, по которым чаще всего выполняется поиск, и по ним создать индексы Если в условии поиска есть выбранные реквизиты, то использовать соответствующий индекс, иначе - цикл по всей таблице. Все равно так будет оптимальнее, чем создание индекса с for А самый оптимальный вариант - использовать клиент-сервер aka ads/letodb. В этом случае таблицу будет молотить сервер, а клиент только получит результат выборки без лишней загрузки сети.

Andrey: Pasha пишет: А самый оптимальный вариант - использовать клиент-сервер aka ads/letodb Пробую. Перевести одну программу несложно, но у меня их несколько. Сейчас занимаюсь чисткой-подгонкой под LetoDB. Работы уйма. Тем более раньше на Клипере из-за ограничения открытия файлов, приходилось открывать БД когда нужно было. Сейчас смотрю все пишут по другому: открывают сразу все базы, а потом работают с ними.

Vlad04: Условные индексы в некоторых случаях имеют право на существование. Я их применяю для построения эпизодических выборок( т.е не каждый день нужны и да же не каждую неделю). Если создать постоянные индексы со всеми вариантами построения условных, то количество индексов удвоится или утроится. А зачем? Это будет в целом тормозить работу ( обновление всех индексов). Потом в условие создания индекса можно "запихнуть" практически любые требования и они отрабатываются. При количестве записей до 200 тыс. время создания несущественно. Короче, в каждом конкретном случае надо смотреть. И хорошо, что есть выбор

Andrey: Некоторые непонятки с условной индексацией. Есть код: nKolRecords := LASTREC() cIndexTo := "PADL(ALLTRIM(FIELD->CNumKvar),5)" cFilterTo := "NN==490.AND.!DELETED()" ---------------------------------------------------------------------------------- INDEX ON &cIndexTo TAG "ONE" TO ("temp.cdx") ; EVAL SAY_PROC() ; EVERY nKolRecords / 10 ; WHILE &cFilterTo ADDITIVE // замена на FOR &cFilterTo ADDITIVE увеличивает построение индекса на 6 сек. ORDSETFOCUS( "ONE" ) DBSETORDER(INDEXORD()) DBGOTOP() где SAY_PROC() - функция рисования бегунка, возвращает всегда .T. Если в базе записи идут в таком порядке: запись запись запись удаленная запись запись запись запись То получается НЕПОРЯДОК - в tbrowse затем отображаются ВСЕГО 3 записи. Если WHILE заменить на FOR то в tbrowse отображаются 6 записей. Как быть ? Что можно сделать чтоб при WHILE выводилось все 6 записи ?

Pasha: Ничего При таком условии при использовании while в индекс попадут 3 записи

Pasha: Андрей, почему бы не сделать постоянный условный индекс с выражением: cIndexTo := "Str(NN)+PADL(ALLTRIM(FIELD->CNumKvar),5)" и условием cFilterTo := "!DELETED()" и в для бровса делать scope по Str(490) ?

Andrey: Pasha Спасибо БОЛЬШОЕ за подсказку !!! Ну не сообразил. Все еще живу ДОСовской реализацией, где каждый индексный файл - это лишнии ресурсы компа. Уже года 3 мучаюсь, как сделать просто (без правки кода) и быстро. Только как будет выглядеть код ? Набросай пожалуйста примерно...

Pasha: Как я понял, пример нужен и для создания индекса, и для scope Индексировать можно примерно так: use тратата if File(cIndexFile) // если файл существует - просто открываем его OrdListAdd(cIndexFile) else /* иначе - создаем заготовим массив вида: aIndex := {; {<tagname1>, <indexp1>, <for1>},; {<tagname2>, <indexp2>, <for2>},; ... со всеми индексами */ OrdListClear() for each ai in aIndex if ! Empty(ai[3]) // если задан for OrdCondSet(ai[3], &('{||'+ai[3]+'}')) endif OrdCreate(cIndexFile, ai[1], ai[2], &('{||'+ai[2]+'}')) next OrdSetFocus(1) endif этот фрагмент можно улучшать по своему вкусу: добавлять еще параметры индекса Если индексный файла существует, можно проверить, все ли нужные индексы в нем есть, и если не все - переиндексировать пример для бровса: set order to <номер> set scope to Str(490, <длина поля>) browse()

Andrey: Pasha пишет: browse() А в нем не нужно никаких хитрых кодоблоков ? Паша спрасибо БОЛЬШОЕ за пример !!!

Pasha: Andrey пишет: А в нем не нужно никаких хитрых кодоблоков ? Для навигации - не нужно, scope установит фильтр по индексу

Andrey: Понятно. С этим разобрались. Теперь другой вопрос по теме. Если индекс "слетает", то как это можно отловить ? А то у меня частенько на старых машинах индексы рушаться.... Если делать каждый раз условную индексацию, то знаешь наверняка что данные выберутся правильные. А если надеяться на индекс, то при "слете" его будет вопеж юзеров - куда делись мои квартиры ...

Vlad04: Попробуй запросы ADS. База у тебя не такая уж и большая. Условия можно сочинять любые. Индексы , конечно помогут, но достаточно наличия по основным полям. Во многих случаях запросы для отчетов - это то что надо.

Andrey: Vlad04 пишет: Попробуй запросы ADS. Спасибо, но я не могу его использовать. Причины: он платный, и нужна еще отдельная локальная версия. На LetoDB буду переходить. Но там пока индексы CDX (по отдельным файлам) не доделали ....

Vlad04: Причины: он платный, Для отчетов можно использовать локальную версию - она бесплатная

Pasha: Vlad04 пишет: Для отчетов можно использовать локальную версию - она бесплатная А смысл ? Для сети это будет тот же файл сервер, причем работать он будет не лучше, если не хуже, чем DBFCDX Только ради sql-запросов ? Да и если запрос сделать неоптимально, он будет выполняться намного медленнее. А чтобы его сделать оптимально, надо понимать, как его будет выполнять Ads, сможет ли он использовать индексы Да и использование двух движков - ads для отчетов, dbfcdx для остального - это просто абсурд

Vlad04: А смысл ? Если результат, данные нескольких связанных баз, смысл применять ADS есть И код получается намного проще.Скорость, как правило в этих случаях не критична. Или 0.5 сек или несколько сек не так важно(нареканий не было). Если же отчет - результат простого перебора данных таблицы , то ads не применяю. Так ads в паре с dbfcdx применяю давно.

Pasha: Речь идет об локальном или настоящем Ads ? Если о настоящем, то он безусловно лучше, чем dbfcdx, если его конечно правильно использовать Если о локальном - то безусловно хуже, поскольку мы имеем лишний слой в виде ace - adslocal Дальше. Разговор здесь не шел о простоте кода в ущерб быстродействию. Наоборот, мы говорили об оптимизации тяжелых операций. И простотой кода в таких случаях надо жертвовать. А использование sql в ads local в случае сложной выборки из нескольких таблиц не спасет. В лучшем случае ads будет делать выборку тем же способом, как она делается ручками в dbfcdx. А может и хуже. Но правильно построенную выборку средствами dbfcdx с помощью ads local превзойти не получится. Совместное использование и ads, и dbfcdx я считаю бессмысленным Если есть ads server, то надо использовать только его. Если нет - то смысла в использовании ads local нет. Но если уж хочется иметь такой суррогатный sql, то надо и для всех операций использовать только ads local. Тогда dbfcdx не нужен.

Andrey: Vlad04 пишет: Если результат, данные нескольких связанных баз, смысл применять ADS есть И код получается намного проще У меня несколько связанных баз. Смысла переделки под ADS нет, так как проще все делается на DBFCDX и времени нет по переходу на него. Я знаю, что локальный ADS будет медленней чем DBFCDX (в форумах давно читал). А на настоящий переходить нельзя - лицензию заказчики оплачивать не будут, ради 3-10 рабочих мест. Так что изучаем лучше матчасть - DBFCDX ....

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

Dima: IndexKey

Andrey: Dima пишет: IndexKey Не то ! То же самое что и : cText := " Ключ индекса: [" + DBORDERINFO( DBOI_EXPRESSION ) + "]" + CRLF

Dima: &(indexkey())

VitalClip: Pasha пишет: Андрей, почему бы не сделать постоянный условный индекс с выражением: cIndexTo := "Str(NN)+PADL(ALLTRIM(FIELD->CNumKvar),5)" . . . Мне кажется Str(NN) немного не корректно ... нужно PADL()



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