busqueda lenta

Post Reply
diegopolverelli
Posts: 149
Joined: Thu Jun 21, 2007 3:26 pm

busqueda lenta

Post by diegopolverelli »

Hola. Tengo un programa que efectura una busqueda de la siguiente manera:

tengo un listbox con un keyseek, donde puedo buscar a medida que escribo, y debajo un get para ingresar un texto, y un boton que dice "FILTRAR"; si pongo en el get, "tornillo", por ejemplo, hago un RAT de tornillo sobre la base del listbox, y muestro solo eso. El problema es que esto lo uso para articulos; cuando trabajo con bases de 200, 300, 1000, 2000, funciona perfecto, pero tengo negocios donde hay mas de 30000 productos, y cuando uno aprieta "BUSCAR", el programa tarda varios minutos en efectuar la busqueda. ¿se les ocurre como hacer esto mas rapido?

SET FILTER TO RAT(UPPER(ALLTRIM(XFILTRO)),UPPER(BUSQ->DESCRIP))<>0

XFILTRO lo escribe el usuario, y la accion se ejecuta al apretar un boton; BUSQ es la base que muestro en el listbox.

sobre la base que muestro en el listbox; funciona de 10, pero arriba de los 15000 productos, tarda muchisimo.

Basicamente tengo que filtrar, o mostrar solamente productos que en una parte de la descripcion tengan un cierto texto, que el usuario puede tipear por pantalla.

Gracias.
User avatar
sambomb
Posts: 385
Joined: Mon Oct 13, 2008 11:26 am
Location: Itaocara - RJ - Brasil
Contact:

Re: busqueda lenta

Post by sambomb »

você faz atualização de indices regularmente no sistema?
Poste o código da sua busca, e já posso adiantar que busca incremental não é uma boa idéia....
Email: SamirSSabreu@gmail.com
MSN: SamirAbreu@hotmail.com
Skype: SamirAbreu
xHarbour 1.1.0 + FwXh 8.02
xHarbour 1.2.1 + Fwhh 10.6
diegopolverelli
Posts: 149
Joined: Thu Jun 21, 2007 3:26 pm

Re: busqueda lenta

Post by diegopolverelli »

Mantengo un indice que actualizo regularmente. Te copio la funcion. Gracias...!!!

funcion de busqueda:

// -----------------------------------------------------------------------
FUNCTION Buscaparam(xtipo, xvalor, xorden, xvta, xcododes, XUSERID)

local olbx, odlg
local obt1, obt2, obt3, xrta
local xok
LOCAL oString
LOCAL XBUSCA, OBUSCA
LOCAL OFNTARIAL
local xorden1, oorden1, xloncc, xcoti1, XHINCHA

DEFINE FONT OFNTARIAL
OFNTARIAL:=FUENTE(XUSERID)

if xcododes='C'
xorden1:=1
else
xorden1:=2
endif

oString = ""
sele 99
use &empre\monedas.sx shared
loca for monedas->moneda="DO"
if !eof()
xcoti1:=monedas->cotizacion
else
xcoti1:=3.3
endif

SELE 99
USE C:\TEMPO\F2F3
F2F3->MARCA:=0
USE


XRTA:=SPACE(16)
XBUSCA:=SPACE(40)
xok:=0
oString := ""
xloncc:=1

DEFINE DIALOG oDlg RESOURCE "REPORT_BUSQUEDA"


REDEFINE RADIO oorden1 VAR xorden1 ID 102,103 of oDlg ;
ON CHANGE (busqORDEN(Xtipo, XORDEN1, XORDEN, xvta, xloncc, olbx))

SetKey( VK_RETURN, { || xrta:=BUSQ->CODCPT, xok:=1, ODLG:END() } )

use &empre\CONLIQ index &empre\conliq alias BUSQ shared

go top
REDEFINE LISTBOX olbx FIELDS BUSQ->CODCPT,BUSQ->DESCRIP ;
HEADERS "Concepto","Descripcion" ;
FIELDSIZES 80, 200 ;
ID 101 OF oDlg ;
ON DBLCLICK ( xrta:=BUSQ->CODCPT, xok:=1, ODLG:END() ) ;
ON CLICK ( xrta:=BUSQ->CODCPT )



oLbx:bkeyChar :={|nKey| KeySeek(nKey,oLbx,390) , ;
oLbx:SetFocus(),oLbx:Refresh() }




REDEFINE GET oBUSCA VAR xBUSCA ID 130 of oDlg FONT OFNTARIAL

REDEFINE BUTTON oBt3 ACTION (FILTRAR(XBUSCA,XTIPO),OLBX:GOTOP(),OLBX:REFRESH()) ID 118 of oDlg


REDEFINE BUTTON oBt1 ACTION ( xok:=1, odlg:end() ) ID 1 of oDlg
REDEFINE BUTTON oBt1 ACTION ( xok:=2, odlg:end() ) ID 2 of oDlg

ACTIVATE DIALOG oDlg centered


y el filtro hace esto:


SELE 390
SET FILTER TO RAT(UPPER(ALLTRIM(XFILTRO)),UPPER(BUSQ->DESCRIP))<>0
GO TOP

y un UPSTABLE sobre olbx


El problema es que CONLIQ (la base de datos), tiene 40000 registros...


Gracias.
User avatar
ADBLANCO
Posts: 299
Joined: Mon Oct 22, 2007 3:03 pm
Location: Valencia - Venezuela

Re: busqueda lenta

Post by ADBLANCO »

Prueba con algo como esto (indices temporales)

(Vas a tener que analizarlo un poquito, y no envío el recurso, pero te debe servir de algo

Code: Select all

memvar cOrden,cIndice,cIndkey,lUnique

*********************************************************************************************************************
function consulta(oCampo,cNomCampo,oDlgAnt,Clave)
*********************************************************************************************************************
 local    oDlg      ,;
          oLbx      ,;
          bSelect   ,;
          bfiltro   ,;
          cRfiltro  ,;
          bCancel   ,;
          bActivos  ,;
          cAlias    :="CONSUL",;
          xGetdata  ,;
          oButOk    ,;
          oButCancel,;
          oGetFiltro,;
          cGetFiltro:=space(38),;
          nOldArch  :=select(),;
          cFiltro    ,;
          cDfiltro   ,;
          cDispfield ,;
          cArch     :="CONSUL",;
          cTitulo   :="Consulta",;
          lExiste   :=.t.,;
          lUArch    :=.t.,;
          oCbx,;
          lCbx,;
          oSay ,;
          oFKey
PRIVATE cOrden,cIndice,cIndKey,lUnique:=.f.
  DEFAULT oDlgAnt:=oWnd
  oFKey:=tvkey():NEW   // CLASE TVKEY (control de teclas de funcion)
  cursorwait()
  cNomCampo :=Upper(cNomCampo)
  cFiltro   :=""
  cDfiltro  :=""
  cDispfield:=""
  bSelect   :=""
  cRfiltro  :=""
  IF Pcount()<4
    Clave:=""
  ENDIF
  DEFINE DIALOG  oDlg  ;
         FONT oFontGen;
         RESOURCE "Consulta" OF oDlgAnt

    oDlg:lHelpIcon:=.f.
    do case
      case cNomcampo="MTITU"
        n_use(DDIR+"TITULAR","=",compartido,1,,"CONSUL")
        CONSUL->(DBSETORDER("ORD1"))
        CONSUL->(DBGOTOP())
        cTitulo   :="Consulta de Titulares"
        cfiltro   :=""
        cDfiltro  :=""
        xGetdata  :=""
        cDispfield:="CONSUL->NOMBRE"
        cIndice   :=DDIR+'TITULAR'
        cOrden    :="ORD1"
        cIndKey   :="_field->CEDLETRA+STR(_FIELD->CEDULA,10)+_FIELD->TIPPER"
        bSelect   :={ || xGetdata:=CONSUL->CEDLETRA+'-'+STRZERO(CONSUL->CEDULA,10),;
                         oCampo:varput(xGetdata),;
                         oCampo:Refresh(),;
                         oDlg:End }
        bCancel   :={ || xGetdata:="",;
                         oDlg:End }
        REDEFINE LISTBOX oLbx ;
                 FIELDS   CONSUL->CEDLETRA+'-'+STRZERO(CONSUL->CEDULA,10),CONSUL->NOMBRE;
                 ALIAS cAlias ;
                 HEADERS "Cédula","Nombre";
                 FIELDSIZES 85,150;
                 ID 401   OF oDlg
      OTHERWISE
         lExiste:=.f.
    endCase
    if lExiste
      SETKEY(VK_ESCAPE, { || eval(bCancel) })
      SETKEY(VK_F4,     { || IIF(oButOk:lActive .AND. !EMPTY(cDispfield),eval(bFiltro),) })
      oLbx:bLdblClick :={ || eval(bSelect) }
      oLbx:bKeyDown   :={ | nKey |  IIF(nKey==VK_RETURN,EVAL(bSelect),IIF(nKey==VK_ESCAPE,EVAL(bCancel),)) }
      oLbx:cMsg:="Doble Click o [Enter] => Selecciona"
      bFiltro:={|| oButOk:Disable(),;
                   oButCancel:Disable(),;
                   cGetFiltro:=LEE_FILTRO(oDlg,cGetFiltro),;
                   oGetFiltro:varput(cGetFiltro),;
                   oGetFiltro:Refresh(),;
                   oButOk:Enable(),;
                   oButCancel:Enable(),;
                   Valfiltro(cGetFiltro,oDlg,cDfiltro,cRfiltro,cDispfield,cAlias,oLbx),oLbx:setfocus(.t.)}
      REDEFINE  BUTTON  oButOk       ID  101  OF oDlg;
                MESSAGE "Indicar Busqueda";
                PROMPT  "[F4]-Buscar";
                ACTION  eval(bFiltro);
                WHEN    !EMPTY(cDispfield)
      REDEFINE  BUTTON  oButCancel    ID  102  OF oDlg;
                PROMPT  "[Esc]-Salir";
                MESSAGE "Cancela la Ayuda";
                ACTION  (eval(bCancel))
      oDlg:cTitle:=ctitulo
      REDEFINE  GET         oGetFiltro      VAR cGetFiltro      ID 201 OF oDlg;
                PICTURE     '@!';
                MESSAGE "Coloque Valor a Buscar, Pulse Ok, (Escape=>Sale, en Blanco=>TODOS)";
                VALID  val_val({||oLbx:setfocus(.t.)});
                WHEN .F.
      cursorarrow()
      IF LEN(ALLTRIM(CLAVE))<>0
        SEEK(CLAVE)
      ENDIF
      oLbx:nHeaderHeight := 31  && Da la altura del header
      oLbx:Set3DStyle()
      ACTIVATE DIALOG oDlg CENTER
      select("consul")
      close
    else
      xGetData:=""
      msgalert("No Existe Ayuda Para Este Campo","Atención...")
  endif
  if nOldArch<>0
    select(nOldArch)
  endif
  oFKey:End()
return IIF(xGetdata=NIL,"",xGetdata)

*********************************************************************************************************************
static function Valfiltro(filtro,oDlg,d_filtro,crfiltro,disp_field,cArch,oLbx)
creafil(filtro,d_filtro,crfiltro,disp_field,cArch,oLbx)
oDlg:refresh()
return .t.

*********************************************************************************************************************
procedure creafil(filtro,d_Filtro,cRfiltro,disp_field,cArch,oLbx)
*********************************************************************************************************************
local bEval,bFiltro,breval
  IF len(alltrim(filtro))=0
    (cArch)->(dbsetfilter())
    (cArch)->(dbgotop())
    ordlistclear()
    ordlistadd(cIndice)
    (cArch)->(ordsetFOCUS(cOrden))
    IF len(alltrim(d_filtro))=0
      (cArch)->(dbsetfilter())
    ELSE
      (cArch)->(dbsetfilter(d_filtro))
    ENDIF
  ELSE
    IF crfiltro=nil
      crfiltro:=""
    ENDIF
    bEval:={ || &disp_field }
    bfiltro:=alltrim(filtro)
    (cArch)->(dbsetfilter())
    (cArch)->(dbgotop())
    ordlistclear()
    ordlistadd(cIndice)
    (cArch)->(ordsetFOCUS(cOrden))
    DELFILE("C:\","TMFILT.CDX")
    IF len(alltrim(crfiltro))=0
      IF lUnique
        INDEX ON &cIndkey to C:\TMFILT.CDX for bFiltro$eval(bEval) UNIQUE
      ELSE
        INDEX ON &cIndkey to C:\TMFILT.CDX for bFiltro$eval(bEval)
      ENDIF
    ELSE
      breval:={ || &crfiltro }
      IF lUnique
        INDEX ON &cIndkey to C:\TMFILT.CDX for bFiltro$eval(bEval) .and. eval(breval)  UNIQUE
      ELSE
        INDEX ON &cIndkey to C:\TMFILT.CDX for bFiltro$eval(bEval) .and. eval(breval)
      ENDIF
    ENDIF
  ENDIF
  (cArch)->(dbgotop())
  oLbx:refresh()
RETURN

*********************************************************************************************************************
STATIC FUNCTION LEE_FILTRO(oDlgAnt,cGetFiltro)
*********************************************************************************************************************
LOCAL oDlg,oGetFiltro
  DEFINE DIALOG  oDlg  ;
         FONT oFontGen;
         RESOURCE "Consul_filtro" OF oDlgAnt;
  oDlg:lHelpIcon:=.f.
  REDEFINE  GET         oGetFiltro      VAR cGetFiltro      ID 201 OF oDlg;
            PICTURE     '@!';
            MESSAGE "Coloque Valor a Buscar, Pulse Enter, (Esc=>Sale, En Blanco=>TODOS)";
            VALID  val_val({||oDlg:End()})
  oGetFiltro:bKeyDown:={ |nKey|  iiF(nKey==VK_RETURN,oDlg:END(),)}
  oGetFiltro:bLDblClick:= {|| oDlg:End()}
  ACTIVATE DIALOG oDlg
  RETURN cGetFiltro

Saludos

Angel, Valencia, Venezuela

xH .997 - FW 7.9 - BCC55 - WorkShop - MySql
User avatar
FranciscoA
Posts: 1964
Joined: Fri Jul 18, 2008 1:24 am
Location: Chinandega, Nicaragua, C.A.

Re: busqueda lenta

Post by FranciscoA »

Francisco J. Alegría P.
Chinandega, Nicaragua.

Fwxh1204-MySql-TMySql
diegopolverelli
Posts: 149
Joined: Thu Jun 21, 2007 3:26 pm

Re: busqueda lenta

Post by diegopolverelli »

Vi este POST, y me interesa, pero no entiendo bien el funcionamiento; ¿alguien tiene algun ejemplo? como para cargar un dbf en memoria y filtrar con at() todos los registros que contengan cierto texto en un campo. Gracias.


La función At() es extremadamente rápida ya que practicamente esta implementada al 100% en el propio microprocesador.

Lo que es lento es ir leyendo los registros de uno en uno. La solución a esto, aunque suene increible, es cargar de una vez toda la DBF en memoria usando MemoRead() y entonces hacer el At():

cDatos = MemoRead( "nombre.dbf" )
nPos = At( "lo que busco", cDatos )

Dividiendo el valor devuelto por At() por el tamaño de un registro (+ el tamaño de la cabecera) sabemos inmediatamente en que registro estamos. El siguiente At() se haría a partir de donde se encontró la primera ocurrencia.

Está técnica la implementamos en su día en una base de datos "documental" y los resultados fueron espectaculares. Espero que te sirva :-)

Si la base de datos es extremadamente grande, se iría leyendo en bloques grandes de memoria.
Loren
Posts: 458
Joined: Fri Feb 16, 2007 10:29 am
Location: Cadiz - España

Re: busqueda lenta

Post by Loren »

diegopolverelli,
el problema te lo origina claramente el SET FILTER que son claramente muy lentos y más todavía si tu aplicativo funciona en RED. Para ello, una solución muy eficaz y ultrarápida son la utilización de indices CDX (no NTX) y la sustitución de los SET FILTER TO por ORDSCOPE. Hacen búsquedas y filtros de forma instantaneas.

Para más información sobre su funcionamiento busca en este foro por ORDSCOPE
Un saludo.
LORENZO
Post Reply