Page 1 of 2
Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 10:25 am
by manuelcalerosolis
Hola a tod@s
Me estoy encontrando con una problemática q nunca se me había dado y es q tengo un cliente q manipula una gran cantidad de registros en sus operaciones.
Por ejemplo puede llegar a meter 275.000 lineas de registros en los números de series de una factura, según mi sistema de mantenimiento yo elimino los registro de la base de datos anterior a la edición y a renglón seguido añado las lineas a la tabla definitiva desde una tabla temporal.
Pero claro en tanto numero de registros los tiempo se hacen enormes, y me preguntaba si hay manera de realizar el borrado de registros de 275.000 registros de manera muy muy rápida, yo actualmente lo hago así.
if cKey != nil
while ::oDbf:Seek( cKey )
if ::bOnPreDeleteDetail != nil
Eval( ::bOnPreDeleteDetail, Self )
end if
::oDbf:Delete( .f. )
if ::bOnPostDeleteDetail != nil
Eval( ::bOnPostDeleteDetail, Self )
end if
end while
end if
pero claro el bucle puede ejecutarse 275.000 veces o mas, como puedo borrar de un tirón todos los registros q cumplan una condición de manera mas efectiva.
Gracias.
Re: Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 11:13 am
by hmpaquito
Manuel,
Necesitas un indice por la condicion de borrado y para mas velocidad no uses la clase TDatabase, sino las primitivas rdd.
El bucle basico es el de siempre:
Code: Select all
// Ejemplo
#Define lCONDICION_BORRADO (TotalCobrado > TotalFactura)
/*
El indice (permanente) que se ha de crear es
INDEX ON If(lCONDICION_BORRADO, "S", "N") TO Nombre_indice
donde lCONDICION_BORRADO se explica por sí misma
*/
SELECT (cAlias)
// nOrderIndiceCondicionBorrado: Orden para recorrer SOLO los que cumplen la condicion de borrado
SET ORDER TO nOrderIndiceCondicionBorrado
SEEK "S"
DO WHILE !Eof() .AND. lCONDICION_BORRADO
Rec_Lock(0)
DELETE
SKIP
ENDDO
UNLOCK
Saludos
Re: Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 11:20 am
by manuelcalerosolis
En ese caso perdería tiempo creando el indice, de todas maneras hago la prueba y computo los tiempos.
Gracias.-
Re: Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 12:11 pm
by hmpaquito
Manuel,
En mi anterior post escribí:
"El indice (permanente) que se ha de crear es
INDEX ON If(lCONDICION_BORRADO, "S", "N") TO Nombre_indice."
Es decir, lo que yo te proponia es que añadas de forma permanente ese indice a la coleccion de indices (u ordenes) que tenga la tabla, asi el indice estará preparado para ser usado.
Saludos
Re: Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 12:56 pm
by manuelcalerosolis
Eso no es posible pq los registros q yo borro son los q el usuario decida editar, no puedo tener un indice por cada documento q yo cree.
Saludos.
Re: Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 1:43 pm
by hmpaquito
Manuel,
Es que no entiendo muy bien; vamos a ver:
Si por ejemplo la tabla de marras tiene un campo Editado .T./.F., que indica si un documento ha sido editado, entonces el indice se creara asi:
INDEX ON If(Editado, "S", "N") TO IndEdi
Saludos
Re: Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 9:10 pm
by RodolfoRBG
y que tal haciendolo como se hacia en dBase3:
DELE ALL FOR (Condicion)
PACK
Re: Borrar gran cantidad de registro
Posted: Tue Nov 08, 2011 10:01 pm
by Enrrique Vertiz
Bueno con ese volumen de informacion, mi sugerencia seria trabajar con algun motor como MySQL, ahi las sentencias de borrado son inmediatas, DELETE FROM ...... WHERE y no necesitan apertura exclusiva, ni indices especiales, pero claro habria que migrar, solo una idea ...
Re: Borrar gran cantidad de registro
Posted: Wed Nov 09, 2011 9:21 am
by lucasdebeltran
Manuel,
¿Has cambiado de página?. Apolosoftware ya no funciona.
Un saludo
Re: Borrar gran cantidad de registro
Posted: Wed Nov 09, 2011 11:19 am
by manuelcalerosolis
Lo de pasar a SQL es complicado mi programa tiene muchísimo código fuente me plantee en tiempo pasar a ADS Cliente/Servidor y los resultados q obtube no fueron muy buenos. Quizás sea momento de volver a intentarlo.
Otra idea era pasar a SQLRDD para xH las pruebas q hice me dejaron muy contento.
Lucas si hemos tenido un problema con el proveedor del dominio y ahora estamos en
www.gstrotor.com estamos intentando recuperar nuestro dominio pero de momento no lo hemos conseguido.
Gracias a todos.
Re: Borrar gran cantidad de registro
Posted: Wed Nov 09, 2011 2:06 pm
by MarioG
Manuel;
Esto es lo que desarrollé alguna vez, utilidando DbSix + TDbf, aunque no tengo experiencia con el borrado de una cantidad de registros como el que mencionas
Code: Select all
...
oDbf_2:= fAbreDbf( DB_ANPRC, FALSE ) // Abro la DB en modo exclusivo
// Campo CodItm
MsgMeter( {|oMeter,oTxt| fBorrando( oMeter, oTxt, oDbf_2, cCodigo, nEs, DB_ANPRC, 1 ) }, ;
"", "Borrando...")
oDbf_2:End() // la cierro, porque es de la época q se cerraba luego de su uso
...
// ---------------------------------------------------------------------------------------------
static procedure fBorrando( oMeter, oTxt, oDbf, cCodigo, nEsPadre, nEs, nCampo ) // No todos los parámetros son útiles en este sample
local bMeter, ;
...
// Odómetro
bMeter:= {|| oMeter:Set( nRecNos ), ;
SysRefresh(), ;
nRecNos += if( oMeter:nTotal >= 2500, 10, 1) }
oMeter:nTotal:= oDbf:LastRec()
nRecNos:= if( oMeter:nTotal >= 2500, 10, 1) // segun el total de registros muestra el avance del meter
oTxt:SetText( cMsgMet )
(oDbf:nArea)->( dbEval( {|| if( cCodigo == Left( oDbf:FieldGet( nCampo ), 3), (oDbf:Delete(), nBorrados++ ), ), ;
nEval++, ;
if( nRecNos!=nEval, , Eval( bMeter ) ) } ) )
end
oTxt:SetText( "Se Borraron "+Str(nBorrados,5)+" Registros" )
oMeter:Set( oMeter:nTotal )
oMeter:Refresh()
SysRefresh()
Inkey( 2 ) // Una parada traida de Clipper :)
Aqui no proceso ni con bFor, ni con bWhile porque cada DB estaba ordenada por su cCodigo
Como comentario adicional te comento que, en el Foro, hay un post de alguien que hizo unas comprativas entre procesar eventos con ADSSQL - FOR EACH y, DbEval, y la diferencia era mínima.
Saludos
Re: Borrar gran cantidad de registro
Posted: Wed Nov 09, 2011 3:39 pm
by Carlos Mora
Si usas los métodos de la clase TDatabase para hacer actualizaciones, seguramente se empezarán
a notar todas las cosas extras que hace cada método y de las que muchas veces podemos prescindir.
Prueba a usar el siguiente código, no se me ocurre nada más rápido que esto. No sé bien que hacen
los métodos bOnPreDeleteDetail y bOnPostDeleteDetail, pero si hacen uso de la clase tdatabase
considera hacer el mismo cambio. Haz una prueba y compara los tiempos.
La forma de bloquear y desbloquear es un poco más agresiva, pero seguramente compensa con la diferencia de tiempos.
Que rdd usas? Que clave tiene el índice? Alguna condición FOR? Se me ocurre que por ahí también podría
estudiarse alguna forma de optimización, tipo un FOR !Deleted() o cosas así.
Code: Select all
if cKey != nil
Select ( oDbf:nArea )
FLock() // Acá seguramente tienes algo para bloquear todo el fichero en lugar de la FLock
while dBSeek( cKey ) // con lSoft si corresponde
if ::bOnPreDeleteDetail != nil
Eval( ::bOnPreDeleteDetail, Self )
end if
dBDelete()
if ::bOnPostDeleteDetail != nil
Eval( ::bOnPostDeleteDetail, Self )
end if
end while
dBCommit()
dBUnLock()
end if
Re: Borrar gran cantidad de registro
Posted: Thu Nov 10, 2011 8:38 am
by manuelcalerosolis
Carlos,
Todos los indices tienen la condición !Deleted(), el valor q contiene el indice es la suma de _ series + numero + delación + numero de linea no es muy extenso.
Saludos.
Re: Borrar gran cantidad de registro
Posted: Thu Nov 10, 2011 9:57 am
by hmpaquito
Manuel,
Te intenté ayudar y no respondiste a mi último mensaje. Se conoce que ya has encontrado la solucion a tu problema.
Uno se siente satisfecho por poder ayudar y encontrar el agradecimiento oportuno.
Saludos
Re: Borrar gran cantidad de registro
Posted: Thu Nov 10, 2011 3:10 pm
by ermatica
Hola Manuel,
No se si ya lo tienes solucionado, de todas formas te comento.
Lo del indice en el caso que te expongo si sería obligatorio, pero usaria scope.
He montado una tabla con un indice (.NTX) con 300.000 registros, filtro entre los registros a borrar y en un ordenador en local los tiempos que me muestra
con un msgalert (Time() ) son de 14:57:35 y final 14:57:49. No se si es mucho. El código que usé es:
Code: Select all
cAlias2:=OpenDbf(mPath_Fich+"FHCARCK1.DBF",5,.T.,.F.,.F.,{mPath_Fich+"FHCARCK1.NTX"},"DBFNTX")
If cAlias2=""
return nil
endif
Select (cAlias2)
OrdsetFocus(1)
dbgotop()
ordscope(0,) // quitar scope, filtro o rango
ordscope(1,)
dbgotop()
nReg0 := 1000
nReg1 := 300000
ordscope(0,nReg0) // poner scope, filtro o rango
ordscope(1,nReg1)
dbgotop()
msgalert ( time() )
Do While !Eof()
Do While !Rlock()
msgalert ("Registro en uso")
Return
Enddo
(cAlias2)->(dbdelete())
skip
Enddo
msgalert ( time() )
Un cordial saludo,
Ernesto