FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index Utilities / Utilidades Búsqueda rápida en DBF enorme?
Posts: 1445
Joined: Mon Oct 10, 2005 02:38 PM
Búsqueda rápida en DBF enorme?
Posted: Wed May 13, 2009 06:31 PM
Hola a todos,

Aquí les dejo una función que he estado haciendo estos días (tenía tiempo).

La finalidad es la búsqueda de un valor dentro de una DBF mediante la función At().

Creo que sólo será útil para ficheros enormes; para los más pequeños las funciones de Harbour ya nos valen.

Agradecería que alguién que tenga algún fichero DBF enorme la pruebe y nos diga si és más rápido que con el comando LOCATE.
Yo no puedo hacerlo, no dispongo de ficheros enormes (ni tampoco espacio en el disco, je, je).

Un Saludo y gracias
Carlos G.

Code (fw): Select all Collapse
#include "fivewin.ch"
#include "Dbstruct.ch"
#include "DbInfo.ch"

/* Función para buscar en una DBF (en su orden NATURAL) un valor contenido
   en un campo determinado.
   Simula el comando LOCATE y CONTINUE, pero realizando un AT().
   Devuelve el Recno() ó '0' según encuentre o no el valor a buscar.
   Se asume que la DBF está abierta y se recibe su Alias().

   Su uso es:
   nAtInDbf( cAliasDBF, "APELLIDO", "GARCIA", .F. )

   para continuar la búsqueda con los mismos parámetros:
   nAtInDbf( cAliasDBF, , , .T. )

   Para que nos entendamos, la primera vez vendría a ser un LOCATE, y la segunda
   vez vendría a ser un CONTINUE.

   --------------------------------------------------------------------------------- */

FUNCTION nAtInDbf( cAliasDBF, cFieldDBF, cString, lContinue )

STATIC cMemoDbf       := "", ;
       nBytesToField  := 0, ;
       nBytesEndField := 0, ;
       nOffset        := 0, ;
       cSearch        := "", ;
       cfield         := "", ;
       nRegLocate_1   := 0

Local aEstruct     := {}
Local nPos         := 0

DEFAULT lContinue := .F.

/*
Traza( 1, cAliasDbf )
Traza( 1, ( cAliasDbf )->(DbInfo( DBI_FULLPATH )) )
Traza( 1, cFieldDbf )
Traza( 1, cString )
Traza( 1, lContinue )
*/

If .not. lContinue
    If Len( cString ) < 1
        MsgInfo("Not Data to Search.", "Search Error." )
        Return 0
    Endif
    cMemoDbf       := MemoRead( ( cAliasDbf )->(DbInfo( DBI_FULLPATH )) )
    nOffset        := 0
    cSearch        := cString
    cField         := Upper( cfieldDBF )
    nRegLocate_1   := 0

    nBytesToField  := 0
    nBytesEndField := 0

    aEstruct       := ( cAliasDBF )->(DBSTRUCT())
    AScan( aEstruct, { |aCampo| If( aCampo[ DBS_NAME ] == cField, ;
                                    (nBytesEndField := nBytesToField + aCampo[ DBS_LEN ] + aCampo[ DBS_DEC ], .T.), ;
                                    (nBytesToField := nBytesToField + aCampo[ DBS_LEN ] + aCampo[ DBS_DEC ], .F. ) ;
                                  ) ;
                     } ;
         )

    If nBytesEndField = 0
        MsgInfo("Field not found.", "Search Error." )
        Return 0
    Endif

EndIf

nPos := 0
While nPos <= ( ( cAliasDBF )->(Header()) + (nRegLocate_1 * ( cAliasDBF )->(RecSize()) ) + nBytesToField) .or. ;
      nPos > ( ( cAliasDBF )->(Header()) + (nRegLocate_1 * ( cAliasDBF )->(RecSize()) ) + nBytesEndField)

    nPos      := AT( cSearch, SubStr(cMemoDbf, nOffset + 1) )

    If nPos = 0
        nRegLocate_1 = -1
        Exit
    EndIf

    nOffset := nPos := nPos + nOffSet

    nRegLocate_1 := INT( ( nPos - ( cAliasDBF )->(Header()) ) / ( cAliasDBF )->(RecSize()) ) // Registro anterior al localizado¿?

    If (( nPos - ( cAliasDBF )->(Header()) ) % ( cAliasDBF )->(RecSize())) = 0
        nRegLocate_1 = nRegLocate_1 - 1  // Registro anterior al localizado.
    EndIf

End

Return nRegLocate_1 + 1
//------------------------------------------------------------------

Un Saludo

Carlos G.



FiveWin 25.12 + Harbour 3.2.0dev (r2502110321), BCC 7.7 Windows 11 Home

Continue the discussion