[Lazarus-es] zeos-firebird: ejemplo transacciones

José Mejuto joshyfun en gmail.com
Vie Mayo 14 21:26:44 CEST 2010


Hello Paola,

Friday, May 14, 2010, 8:36:08 PM, you wrote:

PB> en un principio, más o menos tenía esa estructura.. luego agrupé todas
PB> las sentencias en un procedimiento y ahí fue el lío.
PB> Dentro de ese procedimiento, las sentencias no están dentro del 
PB> try/except.. están unas a continuación de otras.
PB> Quizás, esa no es la forma más correcta de hacerlo...
PB> Según tu ejemplo, entonces si agrupo todo sería algo así.??

Hmmm, parecido, ya que al ser una función tienes dos opciones para
hacerlo, usando try..except implícito para cada execute, o dejando que
las excepciones en grupo funcionen. Puedes hacerlo de estos dos modos:

if grabar() then begin
  connection.commit
end else begin
  connection.rollback;
end;

function grabar(): boolean
begin
try
  SQL.Execute; //Lo que sea, insert, sp, delete...
except
  result:= false;
  Exit;
end;
try
  SQL.Execute;
except
 result:= false;
 Exit;
end;
result:= true;
end;

Tiene la "ventaja" de que puedes reaccionar diferente a cada excepción
si fuera necesario, o bien:

try
  grabar()
  connection.commit;
except
  connection.rollback;
end;

procedure grabar();
begin
  SQL.Execute; //Lo que sea, insert, sp, delete...
  SQL.Execute;
  [...]
  SQL.Execute;
end;

Si cualquiera de las 3 execute dispara una excepción se ejecuta el
bloque except sin pasar por la linea de commit, con el consiguiente
rollback del grupo except.

El "truco" está en usar el mismo sistema en todos los grupos de
actuaciones. Muchas veces necesitamos hacer un insert de muuuuchos
registros, esto ralentiza todo si está en una transacción, por lo
tanto a veces hay que armarse de paciencia para buscar la combinación
de órdenes de insert para maximizar la velocidad sin comprometer la
estructura de los datos.

Un ejemplo, supongamos que queremos crear un histórico, pero este
histórico puede tener millones de registros. Si los metemos
todos en una trasacción, esto llevará horas, mientras que si hacemos
un commit cada 10000 registros, lo podemos meter en la base de datos
en 10 minutos. En este caso tenemos que vendernos al control manual de
las transacciones y usar algún truco para saber todo lo que hemos
metido por si se necesita borrar. Si la BBDD la diseñamos nosotros
añadimos un campo "Verificado" como GUID, lo que nos servirá de
control de la transacción y haríamos algo como:

var
 j: integer;
 MyTrans: TGUID;
begin
 MyTrans:=GUIDCreate();
 j:=0
 while not DatosEntrada.EOF do begin
   SQL.FieldByName('GUID').asLoQueSea:=MyTrans;
   SQL.FieldByName('Otro').asInteger=DatosEntrada.Loquesea;
   try
     SQL.Insert;
     if j>10000 then begin
       SQL.Commit;
       j:=0;
     end;
   except
     SQL.Rollback;
     SQL.SQL.Text:='DELETE FROM HISTORICO WHERE GUID='+MyTrans;
     try
       SQL.Execute;
       SQL.Commit;
     except
       MessageBox('Algo muy malo ha pasado');
       //Guardamos el GUID en un fichero local, para hacer un borrado
       //más tarde, cuando se sepa por que no ha funcionado la
       //excepción.
     end;
   end;
 end;
end;

La contrapartida de esto es que tenemos que acordarnos de que todos
los SQL que consulten el histórico han de disponer de un "AND GUID is
NULL" en el where o donde corresponda.

-- 
Best regards,
 José





More information about the Lazarus-es mailing list