Conjunto de operaciones consistentes en MySQL.

Hola chic@s,

Soy un poco novato aún con MySQL y me gustaría saber si se puede hacer lo siguiente: quisiera realizar un conjunto de transacciones, consistentes entre ellas, pero que no se hagan persistentes hasta que no se haga un COMMIT en la base de datos ( la idea es hacer el COMMIT cuando el usuario le de a "Guardar" en la interfaz del programa).

Voy a dar los datos de las variables de la base de datos y las sentencias que ejecuto:

- Aplicación monousuario.
SET AUTOCOMMIT = 0;
ISOLATION LEVEL = REPETEABLE READ;

START TRANSACTION;
UPDATE tabla1 VALUES (NULL,informacion) // Donde NULL es un identificador autoincrementable.
// La tabla1 supongamos que está inicialmente vacía.

SELECT MAX(id) FROM tabla1; // SIEMPRE obtengo el valor 0!El update no es detectado => tabla vacía :( .
...
..
COMMIT; // el usuario ha clicado "guardar".

Pues esa es la idea, hacer persistentes un conjunto de operaciones, pero con la posibilidad de que se pueda hacer un rollback o commit dependiendo la ocasión. Pero tal como lo tengo montado, pues no funciona.
No sé si el comando SAVEPOINT de MySQL me podría servir...¡ayuda por favor!

¡Gracias!

Las tablas por defecto en MySQL creo que se crean con el formato MyISAM y éste tipo de formato NO admite transacciones, cono ventaja, es muy rápido leyendo y escribiendo datos.

Para que MySQL acepte transacciones como tal, la tabla debe estar en formato InnoDB

http://dev.mysql.com/doc/refman/5.0/en/innodb.html

http://dev.mysql.com/doc/refman/5.0/es/myisam-storage-engine.html

Una búsqueda por google te hubiera ayudado rápido.

Un saludo y suerte!

Hola MinistroPepon, muchas gracias por contestar.

Mis tablas son InnoDB, se me olvidó decirlo, sorry.
Ya busqué por Google, en especial sobre el manual de MySQL, pero no encontré como reproducir el comportamiento que deseo.

Parece que las explicaciones de esas páginas del manual están más orientadas a :
1.EMPEZAR TRANSACION
2.Realizar un conjunto de operaciones.
3.COMMIT o ROLLBACK.

Yo ese COMMIT lo quiero dejar "al aire" hasta que el usuario no haga clic en "Guardar". Dicho de otro modo, quiero volver a un estado anterior del conjunto de transacciones en MySQL como rollback.

Existe un comando que hace algo parecido pero no me funcionó. La cosa queda así más o menos:

SAVEPOINT punto1
operaciones...
operaciones...
operaciones...
COMMIT o ROLLBACK to punto1.

El problema es que si lo hago seteando AUTOCOMMIT = 0, las diferentes operaciones no son consistentes entre sí, y si pongo AUTOCOMMIT = 1, cada operación se considera una transacción finalizada, lo que implica que todos los SAVEPOINTs anteriores a esa operación son eliminados. Entonces, el Rollback al punto1 cuando llega, si se da el caso, ya no existe.

http://dev.mysql.com/doc/refman/5.0/es/savepoints.html

Aún con ésta explicación, ¿crees que tengo que seguir buscando por las páginas del manual que me has sugerido, o quizás lo que estoy buscando se acerca más al uso de SAVEPOINTs?

Un saludo.

darkendymion escribió:

Hola MinistroPepon, muchas gracias por contestar.

Mis tablas son InnoDB, se me olvidó decirlo, sorry.
Ya busqué por Google, en especial sobre el manual de MySQL, pero no encontré como reproducir el comportamiento que deseo.

Parece que las explicaciones de esas páginas del manual están más orientadas a :
1.EMPEZAR TRANSACION
2.Realizar un conjunto de operaciones.
3.COMMIT o ROLLBACK.

Yo ese COMMIT lo quiero dejar "al aire" hasta que el usuario no haga clic en "Guardar". Dicho de otro modo, quiero volver a un estado anterior del conjunto de transacciones en MySQL como rollback.

Existe un comando que hace algo parecido pero no me funcionó. La cosa queda así más o menos:

SAVEPOINT punto1
operaciones...
operaciones...
operaciones...
COMMIT o ROLLBACK to punto1.

El problema es que si lo hago seteando AUTOCOMMIT = 0, las diferentes operaciones no son consistentes entre sí, y si pongo AUTOCOMMIT = 1, cada operación se considera una transacción finalizada, lo que implica que todos los SAVEPOINTs anteriores a esa operación son eliminados. Entonces, el Rollback al punto1 cuando llega, si se da el caso, ya no existe.

http://dev.mysql.com/doc/refman/5.0/es/savepoints.html

Aún con ésta explicación, ¿crees que tengo que seguir buscando por las páginas del manual que me has sugerido, o quizás lo que estoy buscando se acerca más al uso de SAVEPOINTs?

Un saludo.

Si estás usando un lenguage específico, mira a ver si la propia Librería de cliente MySQL tiene un método que establezca el autocommit a false. Quizás si lo pones como una consulta no te funcione puesto que es la librería la que lo realiza.
Dinos en que lenguaje estas trabajando y que librería de acceso a la base de datos usas.

Un saludo.

mmmmh... ¿y el Begin?

toniusco escribió:

Si estás usando un lenguage específico, mira a ver si la propia Librería de cliente MySQL tiene un método que establezca el autocommit a false. Quizás si lo pones como una consulta no te funcione puesto que es la librería la que lo realiza.
Dinos en que lenguaje estas trabajando y que librería de acceso a la base de datos usas.

Un saludo.

Toniusco, es posible que los tiros vayan por ahí.

  • Programo en Builder C++ 2010 ( ¡no porque quiera, si no porque así lo quieren! :) ).
  • Para conectar con MySQL, Builder usa la biblioteca dbExpress http://es.wikipedia.org/wiki/DBExpress.
  • El driver que usa para acceder a MySQL es el libmysql.dll; que viene con el servidor MYSQL 5.1.

Lo curioso es que he conseguido reproducir el comportamiento que quiero desde la consola de MySQL, pero no desde el el código fuente del programa. Ha sido así:

SET AUTOCOMMIT = 0;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
UPDATE tabla1 VALUES (info,NULL); // NULL = identificador autoincrementable
SELECT MAX(id) FROM tabla1; // Ahora sí devuelve bien la información.
....
COMMIT o ROLLBACK // funciona OK!
BEGIN;
.......

Y la cosa curiosa es que el "AUTOCOMMIT = 0" si lo detecta desde el código fuente, y el isolation level serializable también, porque haciendo pruebas y poníendolo a level UNCOMMITED READ; he conseguido bloquear el programa. Es como si no detectará la sentencia BEGIN , o START TRANSACTION...es muy raro...

query->SQL->Add("SET AUTOCOMMIT = 0");
query->ExecSQL();
query->Clear();

query->SQL->Add("set transaction isolation level serializable");
query->ExecSQL();
query->Clear();

query->SQL->Add("BEGIN");
query->ExecSQL();
query->Clear();

query->SQL->Add("INSERT INTO proyectos VALUES ('"+ aux->Strings[0]+ "','"+aux->Strings[1]+"','"+aux->Strings[2]+"',NOW(),NULL)");
query->ExecSQL();
query->Clear();

query->SQL->Add("SELECT MAX(idProyecto) idProyecto FROM proyectos");
query->ExecSQL();
query->Open();

idProyectoCargado =  query->FieldByName("idProyecto")->AsInteger;

/** idProyectoCargado vale 0 siempre porque no se crea ninguna fila en el insert anterior :'( */

Todo éste código, con AUTOCOMIT = 1 y sin el BEGIN y el isolation level, funciona a las mil maravillas, pero no puedo implementar así el comportamiento que he descrito en los posts anteriores.

Gracias mil por contesar.

una pregunta:

el "query->Clear();" no cerrará la conexión, verdad?

supongo por todo loq ue dices que ya has comprobado lo anterior que te comenté, que la tabla MySQL sea InnoDB

MinistroPepon escribió:

una pregunta:

el "query->Clear();" no cerrará la conexión, verdad?

supongo por todo loq ue dices que ya has comprobado lo anterior que te comenté, que la tabla MySQL sea InnoDB

No, tan solo limpia la lista de strings que guarda la query (la consulta SQL).
La conexión propiamente dicha, se encuentra en query->SQLconnection ( o algo así, no recuerdo porque estoy en Debian ahora ); pero vaya, que ese campo guarda la conexión MySQL; la cuál tiene seteado a cierto el atributo "keep connection active".

Si, las tablas son InnoDB.

De todas maneras me has dado una idea: mirare el log del MySQL a ver si el programa no está cerrando la conexión con cada consulta por cualquier otro motivo; y eso implique que acabe la transacción tras la consulta y haga fallar todo.

¡Buenas noches!