FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin para Harbour/xHarbour Como encontrar una cadena en todos los reg de una o mas DBFs
Posts: 2170
Joined: Fri Jul 18, 2008 01:24 AM
Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Sun Aug 17, 2008 08:18 PM

Navegando por este foro el dia de hoy, encontré a dos usuarios que buscaban solución a como encontrar y mostrar un dato en todos los registros de una o más DBFs. (incluso en todos los SubDir que se deseen, con una minima modificación)

Picado por la curiosidad y las ganas de ejercitarme un poco, procedí a crear las siguientes funciones que espero les sean de mucha utilidad para integrar sus propias busquedas.

Cualquier mejora es bienvenida.

Saludos.

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

/ Ejemplo de busqueda en todos los campos de las DBFs deseadas o en un subdirectorio
segun el tipo de dato requerido. (caracter,numerico o fecha)
Se muestran los resultados con la funcion MsgList(), pero bien pueden re-dirigirse
a una dbf u otro tipo de destino.
Muestra, en MsgList(), el nombre de la DBF, el nombre del campo, el No. de registro y el valor buscado.
Se puede mejorar agregando la busqueda en un campo específico.
Si se utiliza un array con las DBFs definidas y no encuentra alguna, informa al respecto.
/

//Francisco J. Alegría P. :Me gustaría recibir sus mejoras. Saludos. ( falegria230349@yahoo.es )
//Chinandega, Nicaragua.
//Agosto 17/2008

include "FiveWin.ch"

**#include "directry.ch" //por si se usa aDirectory()

static aEncont:={}
//-------------------------------------------//
function Main()
local oDlg, xBuscar, oBuscar, oRadio, nVar:=1

SET DATE BRITISH
SET EPOCH TO 1995
SET CENTURY ON
SET DELETED ON

set resources to "busqueda.dll"

DEFINE DIALOG oDlg RESOURCE "BUSCADOR1" TITLE "Prueba de busqueda de un valor en DBFs"

REDEFINE RADIO oRadio VAR nVar ID 103,104,105 OF oDlg ON CHANGE Refresque(@xBuscar,oBuscar,nVar)

REDEFINE GET oBuscar VAR xBuscar ID 102 OF oDlg

REDEFINE BUTTON ID 201 OF oDlg Action ( if(!empty(xBuscar),MsgRun("Aguarde...",,{||Busque(xBuscar)}), msgStop("Valor omitido")),oBuscar:SetFocus() )
REDEFINE BUTTON ID 202 OF oDlg Action ( oDlg:end() ) CANCEL

ACTIVATE DIALOG oDlg CENTERED

return nil

//------------------------
Function Refresque(xBuscar,oBuscar,nVar)
if nVar=1
xBuscar:=space(20)
elseif nVar=2
xBuscar:=0.0000
elseif nVar=3
xBuscar:=date()
endif
oBuscar:Refresh()
Return nil

//---------------------------
//debe usarse aDirectory() si se desea busqueda en todas las DBFs del subdir.
//---------------------------
Function Busque(xBuscar)
local cTipoDato:=ValType(xBuscar)
local aDBFS:={"test","wmdata"}
local n:=0, cAlias
local TpoIn:=Seconds(), TpoFi:=0, nRegist:=0, nDbfs:=0, nReg:=0

For n:=1 to len(aDBFS)
if !file(aDBFS[n]+".dbf")
aadd(aEncont,"NO SE ENCONTRO "+aDBFS[n]+".dbf")
else
dbUseArea(.t.,,aDBFS[n],aDBFS[n],.t.)
nRegist+=(aDBFS[n])->(Reccount())
nDbfs+=1
Buscando(aDBFS[n],xBuscar,cTipoDato,@nReg)
(aDBFS[n])->(dbCloseArea())
endif
Next

MsgInfo("Total Segundos "+transform(Seconds()-TpoIn,"999,999.99")+chr(13)+;
"Bases de datos escaneadas "+alltrim(str(nDbfs))+chr(13)+;
"Registros encontrados "+transform(nReg,"9,999,999"), transform(nRegist,"9,999,999")+" reg. escaneados")

SysRefresh()

if !empty(aEncont)
MsgList(aEncont, "Registros encontrados",1,1,30,80,"Salir")
else
MsgInfo("No se encontraron coincidencias")
endif
aEncont:={}
Return nil

//------------------------
Function Buscando(cAlias,xBuscar,cTipoDato,nReg)
local n:=0

(cAlias)->(dbgotop())

WHILE (cAlias)->(!eof())
for n:=1 to (cAlias)->(fCount())
if ValType( (cAlias)->(FieldGet(n)) ) == cTipoDato
if cTipoDato = "C"
xBuscar:=Upper(alltrim(xBuscar)) //convertimos a mayusc para asegurar resultado
if At( xBuscar, Upper((cAlias)->(FieldGet(n))) ) != 0 //si encuentra coincidencia
aadd( aEncont, (cAlias)+"->"+(cAlias)->(FieldName(n)) +" (Reg "+Alltrim(str((cAlias)->(Recno()))) +") "+(cAlias)->(FieldGet(n)) ) //lo agregamos a un array
nReg+=1
endif
elseif cTipoDato = "N"
if (cAlias)->(FieldGet(n)) = xBuscar
aadd( aEncont, (cAlias)+"->"+(cAlias)->(FieldName(n)) +" (Reg "+Alltrim(str((cAlias)->(Recno()))) +") "+Transform((cAlias)->(FieldGet(n)),"999,999,999.9999") )
nReg+=1
endif
elseif cTipoDato = "D"
if (cAlias)->(FieldGet(n)) = xBuscar
aadd( aEncont, (cAlias)+"->"+(cAlias)->(FieldName(n)) +" (Reg "+Alltrim(str((cAlias)->(Recno()))) +") "+Dtoc((cAlias)->(FieldGet(n))) )
nReg+=1
endif
endif
endif
next
SysRefresh()
(cAlias)->(dbSkip())
ENDDO

Return nil

Francisco J. Alegría P.

Chinandega, Nicaragua.



Fwxh-MySql-TMySql
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Mon Aug 18, 2008 01:29 AM

FieldGet() y DbSkip() son muchísimo más lentos que usar At() directamente en toda la DBF cargada en memoria.

Al usar At() nos saltamos todo el mecanismo interno de los RDDs y usamos el sistema de búsqueda más rápido que puede hacerse en un bloque de datos.

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 2170
Joined: Fri Jul 18, 2008 01:24 AM
Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Mon Aug 18, 2008 01:25 PM
Antonio Linares wrote:FieldGet() y DbSkip() son muchísimo más lentos que usar At() directamente en toda la DBF cargada en memoria.

Al usar At() nos saltamos todo el mecanismo interno de los RDDs y usamos el sistema de búsqueda más rápido que puede hacerse en un bloque de datos.


Gracias, Antonio.
Saludos.
Francisco J. Alegría P.

Chinandega, Nicaragua.



Fwxh-MySql-TMySql
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Sun Nov 30, 2014 05:25 PM

Can I see a sample test of it ?
I need a search of a word or some words into a field memo or character ...

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Sun Nov 30, 2014 06:16 PM

MsgInfo( At( "Silvio", MemoRead( "MyDbf.dbf" ) ) )

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 7317
Joined: Thu Oct 18, 2012 07:17 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Tue Dec 02, 2014 08:35 AM

Antonio,

I wish insert all Whats new of Fwh into a Dbf

DbCreate(cDir+'FW', {{ "FWANYO" , "C", 4, 0 },; // sample 2014
{ "FWNUMERO" , "C", 2, 0 },; // sample 09
{ "FWWHATS", "C", 250, 0 } } , 'DBFCDX')

AND INSERT ON FWWHATS FIELD THE WHATS NEW COMMENT

Now I wish search a word or a sentence into FWWHATS field and the function must give me ( show on screen)
the year and release and the text where the procedure found the sentence or the word i insert to find.

Since from 1991/1992 ( fw for clipper Rel. 14.4 - Momos)

I use : FiveWin for Harbour March-April 2024 - Harbour 3.2.0dev (harbour_bcc770_32_20240309) - Bcc7.70 - xMate ver. 1.15.3 - PellesC - mail: silvio[dot]falconi[at]gmail[dot]com
Posts: 6755
Joined: Wed Feb 15, 2012 08:25 PM
Re:
Posted: Wed Dec 03, 2014 07:55 PM
Antonio Linares wrote:FieldGet() y DbSkip() son muchísimo más lentos que usar At() directamente en toda la DBF cargada en memoria.

Al usar At() nos saltamos todo el mecanismo interno de los RDDs y usamos el sistema de búsqueda más rápido que puede hacerse en un bloque de datos.


Antonio, sin que sea exactamente el tema que se describe aqui, qué es más rápido?

Code (fw): Select all Collapse
if cSearch $ cString
...


o

Code (fw): Select all Collapse
if !empty( At( cSearch, cString ) )
...
Cristobal Navarro

Hay dos tipos de personas: las que te hacen perder el tiempo y las que te hacen perder la noción del tiempo

El secreto de la felicidad no está en hacer lo que te gusta, sino en que te guste lo que haces
Posts: 1515
Joined: Thu Oct 30, 2008 02:37 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Wed Dec 03, 2014 10:23 PM

Cristobal,

Yo no lo sé pero de entrada se me antoja que At(cSearch, cString) == 0 sea levísimamente mas rapido que Empty( At(cSearch, cString) )

Saludos

Posts: 1445
Joined: Mon Oct 10, 2005 02:38 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Fri Dec 05, 2014 08:54 PM
Hola todos,

No es la solución a lo que mencionan, pero tiene algo que ver y además puede servir para desarrollar lo que se desea.
Imagínense una DBF enorme en la cual deben buscar un valor en un campo el cual no está indexado; pués para este caso les serviría este código:

Por cierto, si alguien crea una clase con ello, pues mejor que mejor; así se podrá utilizar para diversas DBF a la vez.

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.
   Devuelve -1 cuando el campo indicado no existe en la DBF o no se especifica 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 -1
    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 -1
    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

Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Sat Dec 06, 2014 07:22 AM

Cristobal,

Supongo que lo más rápido es

if cSearch $ cString

pero no habrá mucha diferencia entre las dos formas que muestras

regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 1515
Joined: Thu Oct 30, 2008 02:37 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Mon Dec 08, 2014 08:22 PM

Gracias Fivewidi por tu clase... tiene una pinta sensacional... me la guardo para cuando la necesite...
Una funcion asi siempre la quise tener.

Por cierto... una pregunta... ¿ has hecho pruebas tomando tiempos ? ¿ va bien de velocidad ?

Un saludo y de nuevo gracias por el aporte.

Posts: 518
Joined: Fri Jun 29, 2012 12:49 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Tue Dec 09, 2014 07:11 AM

Fivewidi , muy agradecida!!!.

Una dudita, ¿funciona cuando se abre la dbf en modo SHARED??.

Posts: 1445
Joined: Mon Oct 10, 2005 02:38 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Tue Dec 09, 2014 07:25 AM
hmpaquito wrote:Gracias Fivewidi por tu clase... tiene una pinta sensacional... me la guardo para cuando la necesite...
Una funcion asi siempre la quise tener.

Por cierto... una pregunta... ¿ has hecho pruebas tomando tiempos ? ¿ va bien de velocidad ?


No es una clase, no estoy familiarizado en crear clases y esta función la cree para jugar un poco con el código; y si alguién lo quiere convertir en una clase sería de agradecer.
Respecto a la velocidad, pués no tengo ninguna DBF grande para provar, pero tratándose de substiturir un locate y con los comentarios de antonio respecto al uso de AT(), creo que debe mejorarla.

Saludos,

Un Saludo

Carlos G.



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

Posts: 1445
Joined: Mon Oct 10, 2005 02:38 PM
Re: Como encontrar una cadena en todos los reg de una o mas DBFs
Posted: Tue Dec 09, 2014 07:26 AM
elvira wrote:Fivewidi , muy agradecida!!!.

Una dudita, ¿funciona cuando se abre la dbf en modo SHARED??.

Debería funcionar sin problemas y creo que así lo probé en su día.

Saludos,

Un Saludo

Carlos G.



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

Continue the discussion