Borrar gran cantidad de registro

User avatar
manuelcalerosolis
Posts: 149
Joined: Mon Oct 10, 2005 9:30 am
Location: Huelva - Spain

Borrar gran cantidad de registro

Post 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.
hmpaquito
Posts: 1200
Joined: Thu Oct 30, 2008 2:37 pm

Re: Borrar gran cantidad de registro

Post 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
User avatar
manuelcalerosolis
Posts: 149
Joined: Mon Oct 10, 2005 9:30 am
Location: Huelva - Spain

Re: Borrar gran cantidad de registro

Post by manuelcalerosolis »

En ese caso perdería tiempo creando el indice, de todas maneras hago la prueba y computo los tiempos.

Gracias.-
hmpaquito
Posts: 1200
Joined: Thu Oct 30, 2008 2:37 pm

Re: Borrar gran cantidad de registro

Post 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
User avatar
manuelcalerosolis
Posts: 149
Joined: Mon Oct 10, 2005 9:30 am
Location: Huelva - Spain

Re: Borrar gran cantidad de registro

Post 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.
hmpaquito
Posts: 1200
Joined: Thu Oct 30, 2008 2:37 pm

Re: Borrar gran cantidad de registro

Post 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
User avatar
RodolfoRBG
Posts: 253
Joined: Tue May 16, 2006 4:46 pm
Location: San Luis Potosi, SLP, Mexico
Contact:

Re: Borrar gran cantidad de registro

Post by RodolfoRBG »

y que tal haciendolo como se hacia en dBase3:

DELE ALL FOR (Condicion)
PACK
RodolfoRBG
FWH 1307, xHarbour123 BCC582
rodolfoerbg@gmail.com
Enrrique Vertiz
Posts: 440
Joined: Fri Oct 07, 2005 2:17 pm
Location: Lima - Peru
Contact:

Re: Borrar gran cantidad de registro

Post 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 ...
Enrrique Vertiz Pitta
Lima-Peru
xHb 1.23, Fwh 20.04, MySQL 5.7 - 8.0, SQLLIB 1.9m, SQLRDD
User avatar
lucasdebeltran
Posts: 1303
Joined: Tue Jul 21, 2009 8:12 am
Contact:

Re: Borrar gran cantidad de registro

Post by lucasdebeltran »

Manuel,

¿Has cambiado de página?. Apolosoftware ya no funciona.

Un saludo
Muchas gracias. Many thanks.

Un saludo, Best regards,

Harbour 3.2.0dev, Borland C++ 5.82 y FWH 13.06 [producción]

Implementando MSVC 2010, FWH64 y ADO.

Abandonando uso xHarbour y SQLRDD.
User avatar
manuelcalerosolis
Posts: 149
Joined: Mon Oct 10, 2005 9:30 am
Location: Huelva - Spain

Re: Borrar gran cantidad de registro

Post 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.
User avatar
MarioG
Posts: 1356
Joined: Fri Oct 14, 2005 1:28 pm
Location: Resistencia - Chaco - AR

Re: Borrar gran cantidad de registro

Post 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
Resistencia - "Ciudad de las Esculturas"
Chaco - Argentina
Carlos Mora
Posts: 988
Joined: Thu Nov 24, 2005 3:01 pm
Location: Madrid, España

Re: Borrar gran cantidad de registro

Post 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

 
Saludos
Carlos Mora
http://harbouradvisor.blogspot.com/
StackOverflow http://stackoverflow.com/users/549761/carlos-mora
“If you think education is expensive, try ignorance"
User avatar
manuelcalerosolis
Posts: 149
Joined: Mon Oct 10, 2005 9:30 am
Location: Huelva - Spain

Re: Borrar gran cantidad de registro

Post 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.
hmpaquito
Posts: 1200
Joined: Thu Oct 30, 2008 2:37 pm

Re: Borrar gran cantidad de registro

Post 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
ermatica
Posts: 44
Joined: Mon Nov 12, 2007 1:50 pm
Location: España

Re: Borrar gran cantidad de registro

Post 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
Post Reply