Foros del Web » Programación para mayores de 30 ;) » Bases de Datos General » Mysql »

RollBack de varios Insert

Estas en el tema de RollBack de varios Insert en el foro de Mysql en Foros del Web. Hola a todos, me gustaría controlar una serie de acciones mediante RollBack para que, en caso de fallo o error, vuelva al estado inicial como ...
  #1 (permalink)  
Antiguo 19/05/2010, 04:53
 
Fecha de Ingreso: noviembre-2005
Ubicación: Alicante (España)
Mensajes: 242
Antigüedad: 19 años
Puntos: 1
RollBack de varios Insert

Hola a todos,

me gustaría controlar una serie de acciones mediante RollBack para que, en caso de fallo o error, vuelva al estado inicial como si no se hubiera hecho nada.

Tengo dos problemas, uno es que no sé si la sintaxis es realmente como la tengo puesta ya que no lo he utilizado nunca y me gustaría que alguien me explicara cómo utilizar las instrucciones BEGIN, COMMIT y ROLLBACK de forma correcta.

Mi segundo problema es que, además de 2 Insert a 2 tablas distintas de mi base de datos, debo lanzar un mail que, en caso de fallo, me gustaría retrocediera todas las acciones.

Expongo mi código para aclarar el tema:

BEGIN;

INSERT (TABLA1)
INSERT(TABLA2)

include ('envío_mail.php');

ROLLBACK;

COMMIT;


Bien. Así es como lo tengo puesto. ¿Es correcto utilizar las palabras BEGIN, COMMIT Y ROLLBACK de esta forma?

Si el primer INSERT falla, no se ha realizado ningún INSERT y debería ir al COMMIT directamente evitando lanzar el segundo INSERT y el envío del MAIL.

Si falla el Segundo INSERT, debería retroceder el primero con el ROLLBACK y continuar el código después del COMMIT

Ahora bien, si ambos INSERTS funcionan y falla el ENVÍO del mail, me gustaría que retrocediera ambos INSERTS también pero, como el envío del mail está hecho con php y no toca bases de datos, imagino que el rollback no actuará en este caso, ¿no?
¿Es esto correcto?

Otra opción es colocar el envío del mail antes de los dos inserts y fuera del BEGIN pero entonces, se enviará un mail al cliente y, si los inserts fallan, no me gusta la idea de que tenga un mail en su buzón que no puede activar nada ya que, el buen funcionamiento, depende de que las 3 cosas se ejecuten.

¿Hay alguna manera de retroceder los inserts, (rollback), si el mail falla?

Un saludo.
  #2 (permalink)  
Antiguo 19/05/2010, 06:53
Colaborador
 
Fecha de Ingreso: marzo-2008
Ubicación: Sabadell
Mensajes: 4.897
Antigüedad: 16 años, 8 meses
Puntos: 574
Respuesta: RollBack de varios Insert

Creo que este ejemplo ter puede ayudar


Código PHP:
Ver original
  1. $SERVIDOR="TuHost";
  2. $BASE_DATOS="TuBaseDeDatos";
  3. $USUARIO="TuUsuario";
  4. $PASSWORD="TuPassword";
  5.  
  6. $db = mysql_connect($SERVIDOR,$USUARIO,$PASSWORD);
  7. mysql_select_db($BASE_DATOS,$db);
  8.  
  9. $error = 0; //variable para detectar error
  10.  
  11. mysql_query("BEGIN"); // Inicio de Transacción
  12.  
  13. $result=mysql_query("INSERT INTO tabla1 VALUES (1,1000)");
  14.  
  15. if(!$result)
  16. $error=1;
  17.  
  18. $result=mysql_query("INSERT INTO tabla2 VALUES (1,2000)");
  19.  
  20. if(!$result)
  21. $error=1;
  22.  
  23. if($error) {
  24. mysql_query("ROLLBACK");
  25. echo "Error en la transaccion";
  26. } else {
  27. mysql_query("COMMIT");
  28. echo "Transacción exitosa";
  29. }

No es mio visita al autor.

Solo te falta intercalar la gestion del mail y detectar el posible error en la misma variable....


Quim
  #3 (permalink)  
Antiguo 20/05/2010, 00:51
 
Fecha de Ingreso: noviembre-2005
Ubicación: Alicante (España)
Mensajes: 242
Antigüedad: 19 años
Puntos: 1
Respuesta: RollBack de varios Insert

Muchas gracias Quim,

he implementado un código similar al que me has enviado y, ahora mismo, sólo tengo una duda.

Yo utilizo una clase para el tema de la conexión y manejo con la base de datos. Tengo una función para la conexión y varias funciones para realizar todo tipo de consultas, (Selects, Inserts, Updates, etc).

Te adjunto, únicamente la función que utilizo para el insert.

<?

class CBaseDatos{
var $ConexionActiva;
var $BaseDeDatos;

//Constructor
function CBaseDatos($host,$user,$pass,$datab){
$this->ConexionActiva=@mysql_connect($host,$user,$pass );
$this->BaseDeDatos=@mysql_select_db($datab);
}

//Consultas sin resultados (INSERT,DELETE...)
function qryNoRes($consulta){
$Haz=@mysql_query($consulta,$this->ConexionActiva);
}

}//fin de clase

?>


Bien, esta clase funciona perfectamente y realiza el Insert bien.

Yo creo una instancia de esta clase y la utilizo para operar con la base de datos:

$Data = new CBaseDatos(st_host,st_user,st_pass,st_db);

Ahora bien, ¿Cómo utilizo la instancia $Data para indicarle el BEGIN, el ROLLBACK y el COMMIT?
Mi duda es implementar un código que funcione EXACTAMENTE igual que esto:

mysql_query("BEGIN");
mysql_query("ROLLBACK");
mysql_query("COMMIT");


pero con la instancia $Data de mi clase

¿Tengo que crear una función nueva?
¿Algo así?

function transaccion ($punto) {
mysql_query($punto);
}


$Data->transaccion("BEGIN");

$resultado_insert = $Data->qryNoRes($INSERT1);
if (!$resultado_insert) $exito_insert=1;

$resultado_insert = $Data->qryNoRes($INSERT2);
if (!$resultado_insert) $exito_insert=1;

if ($exito_insert == 1) {
$Data->transaccion("ROLLBACK");
}else $Data->transaccion("COMMIT");


Mi duda sería si esta forma de usar la instancia Data de mi clase con la función creada sería correcta para utilizar BEGIN, ROLLBACK y COMMIT.

Un saludo.
  #4 (permalink)  
Antiguo 20/05/2010, 01:47
Colaborador
 
Fecha de Ingreso: marzo-2008
Ubicación: Sabadell
Mensajes: 4.897
Antigüedad: 16 años, 8 meses
Puntos: 574
Respuesta: RollBack de varios Insert

Sí, yo lo haria así.

Una funcion para cada cosa en la clase encargada de crear y gestionar la conexión...



Quim
  #5 (permalink)  
Antiguo 20/05/2010, 03:52
 
Fecha de Ingreso: noviembre-2005
Ubicación: Alicante (España)
Mensajes: 242
Antigüedad: 19 años
Puntos: 1
Respuesta: RollBack de varios Insert

Vale Quim,

pero tengo una última duda ;(

Si resulta que los inserts se realizan correctamente pero el usuario introduce mal la dirección de mail, el mensaje de activación del usuario no se enviará, dejando dos registros en dos tablas incoherentes.
Necesito que, si la dirección no está bien, los inserts se eliminen. Como ROLLBACK de un código php, (así está el envío de mail), no lo puedo hacer, tendré que ejecutar dos DELETES, ¿no?

¿Quedaría bien este código o a alguien se le ocurre una forma más sencilla y óptima?

Function instrucción_segura($punto) {
mysql_query($punto);
}




$Data = new CBaseDatos(st_host,st_user,st_pass,st_db);

$Data-> instrucción_segura ("BEGIN");

$resultado_insert1 = $Data->qryNoRes($INSERT1);
if (!$resultado_insert1) $Data-> instrucción_segura ("ROLLBACK");

$resultado_insert2 = $Data->qryNoRes($INSERT2);
if (!$resultado_insert2) $Data-> instrucción_segura ("ROLLBACK");

// Si los dos se ejecutan correctamente
if ($resultado_insert1) ||($resultado_insert2) {

$Data->instrucción_segura ("COMMIT");
$resultado_mail = Mail->Send();
// Si hay un error al enviar el mail
if (!$resultado_mail) {
// Error al enviar el mail
$Data-> instrucción_segura ("BEGIN");

$resultado_delete1 = $Data->qryNoRes($DELETE1);
if (!$resultado_delete1) $Data-> instrucción_segura ("ROLLBACK");
$resultado_delete2 = $Data->qryNoRes($DELETE2);
if (!$resultado_delete2) $Data-> instrucción_segura ("ROLLBACK");

// Si los dos se ejecutan correctamente
if ($resultado_delete1) ||($resultado_delete2) {
$Data-> instrucción_segura ("COMMIT");
}

}

}
  #6 (permalink)  
Antiguo 20/05/2010, 04:57
Colaborador
 
Fecha de Ingreso: marzo-2008
Ubicación: Sabadell
Mensajes: 4.897
Antigüedad: 16 años, 8 meses
Puntos: 574
Respuesta: RollBack de varios Insert

No ... el ROLLBACK lo tienes que hacer al final de todo el proceso...

//Inicializo el objeto
objConn=new Conn(.....)
//Inicializo la variable de control
error=0;
//Inicializo la transacció
objConn-> instrucción_segura ("BEGIN");
//Hago n operaciones con la bbdd
INSERTs UPDATEs, DELETEs
//Modifico la variable error en funcion del resultado de las operaciones

//Si NO hay error
//Hago otras operaciónes (p.e. mando un mail)
//Tambien registro el posible error en la misma variable


//Cierro la transacción
//Si hay error lanzo el ROLLBACK (este deshara TODAS las operaciones hechas a la bbdd desde el BEGIN)
//Si NO hay error lanzo el COMMIT


Obviamente puedes abortar el proceso con algun break o GO TO si al encontrar el primer error ya invalida todo el proceso...

En realidad como el control del error lo hacemos con una variable externa podemos controlar qualquier error no solo los de bbdd ... así es como si el envio del mail fuera una operación mas dentro de la transacción y por tanto lanzare el ROLLBACK si falla... como si hubiese fallado una operacion de la BBDD.

El ROLLBACK es para deshacer aquello que se ha hecho no lo que no se ha hecho, luego en tu codigo hay algo que no tiene sentido

PRIMERA OPERACION CON LA BBDD
$resultado_insert1 = $Data->qryNoRes($INSERT1);
if (!$resultado_insert1) $Data-> instrucción_segura ("ROLLBACK");
Si FALLA hago ROLLBACK

esto no tiene sentido puesto que si ha fallado no hay nada que deshacer (ROLLBACK--> rodar para atras ->retroceder), en todo caso si la primera operacion te falla lo que tienes que evitar es que se hagan las demas, especialmente el envio del mail, puesto que si este se realiza no habra ROLLBACK posible para retrocederlo... (Si tendria sentido des de el punto de vista de cerrar la transacción)

No se si me he explicado, el sentido del tratamento de transacciones es el de poder "deshacer" aquello que se haya hecho no lo que no se ha hecho!!!

Si despues de una serie de operaciones hechas se produce algo que las hace invalidas deshacerlas TODAS para dejar la BBDD en el estado anterior...


Quim

Edito: Como no estoy seguro de saber explicarme te lo doy hecho
Código PHP:
Ver original
  1. <?php
  2. /*
  3. *Supongamos que
  4. *qryNoRes() retorna true si no hay error
  5. *                   false se se produce un error
  6. * Send()    retorna true si no hay error
  7. *                   false se se produce un error
  8. */
  9.  
  10. $Data = new CBaseDatos(st_host,st_user,st_pass,st_db);
  11. $Data-> instruccion_segura ("BEGIN");
  12. $error=false;
  13.  
  14. if ($Data->qryNoRes($INSERT1)){
  15.     if ($Data->qryNoRes($INSERT2)){
  16.         // Si los dos se ejecutan correctamente
  17.         if ($Mail->Send()){
  18.             $Data->instruccion_segura ("COMMIT");
  19.             echo "Todo correcto";
  20.         }else{
  21.             //Si hay error en el mail
  22.             $error=true;
  23.         }
  24.     }else{
  25.         //Si hay error en el segundo INSERT
  26.         $error=true;
  27.     }
  28. }else{
  29.         //Si hay error en el primer INSERT
  30.         $error=true;
  31. }
  32.  
  33. if($error) $Data-> instruccion_segura ("ROLLBACK");
  34. ?>

Última edición por quimfv; 20/05/2010 a las 05:18
  #7 (permalink)  
Antiguo 20/05/2010, 06:05
 
Fecha de Ingreso: noviembre-2005
Ubicación: Alicante (España)
Mensajes: 242
Antigüedad: 19 años
Puntos: 1
Respuesta: RollBack de varios Insert

Muchísimas gracias Quim!!

Ahora me queda clarísimo. Después de ver el código lo he entendido perfectamente. Mi problema estaba también, en no ver los if anidados.

Al tener el envío del mail anidado con los otros inserts, si este no se envía bien retrocede los otros dos.

Está perfecto. Muchísimas gracias de nuevo!! ;)

Etiquetas: insert, rollback
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta

SíEste tema le ha gustado a 1 personas




La zona horaria es GMT -6. Ahora son las 10:20.