Transacciones en VFP y SQL Server
[Link]
Por Alberto Rodríguez
[Link]
Una transacción propiamente es un acceso a la Base de Datos. Las
Transacciones fueron implementadas en el mundo Fox en la versión 3.0 pero
en otras Bases de Datos existen desde hace tiempo.
Una transacción actúa como un log que controla las operaciones de
actualización de datos en la memoria o en el disco, dependiendo de la
implementación y de los requerimientos del sistema, más bien que aplicando
estas actualizaciones directamente a la base de datos. La actualización de la
base de datos es realizada al final de las transaccion. Si por alguna razón el
sistema no puede realizar la actualización de la base de datos, se puede volver
atrás la entera transacción y no realizar ninguna de las operaciones que se han
realizado.
Las transacciones deben cumplir el Test ACID que es un acrónimo que apareció
por primera vez en 1980 en un documento presentado a la ACM. Desde entonces, las
Bases de Datos tienen que superar el test ACID para decir que soportan completamente
las Transacciones. Las propiedades ACID con: Atomicidad, Consistencia, Aislamiento
y Durabilidad.
· Atomicidad: Una transacción debe grabar todas las
operaciones que se encuentran dentro de ella o eliminarlas todas.
· Consistencia: significa que después de las transacciones los
datos deben quedar consistentes.
· Aislamiento: Cada transacción debe estar aislada del resto de
transacciones: una transacción no puede usar datos de otra
transacción que todavía no ha sido granada.
· Durabilidad: una transacción debe ser guardada
permanentemente con independencia de cualquier tipo de fallo del
sistema: esto es, una transacción debe mantener la unidad de trabajo
guardando los cambios que han hecho el commit o haciendo el
Rollback de toda la transacción.
SQL Server soporta las cuatro pero VFP sólo soporta las tres primeras: no tiene
durabilidad.
Instrucciones
VFP SQL Server
BEGIN TRANSACTION BEGIN TRANSACTION
SET IMPLICIT_TRANSACTIONS ON
END TRANSACTION COMMIT
ROLLBACK ROLLBACK
TnxLevel() @@TranCount
BEGIN TRANSACTION Incia la transacción
ROLLBACK Vuelve a la situación original sin
cambios desde el último BEGIN TRANSACTION
END TRANSACTION/COMMIT Bloquea los registros, graba a disco
todos los cambios hechos en las tablas de las base de datos
desde el más reciente BEGIN TRANSACTION. A continuación
bloquea los registros.
Las Transacciones en Visual FoxPro
Se pueden usar transacciones para asegurar las modificaciones de las
tablas. Si tienes que actualizar dos o más tablas, las transacciones te
garantizan que se podrán actualizar las dos. Si puede actualizar una pero otra
no entonces generará un error y podrás hacer un RollBack.
Un ejemplo de un código que ya se publicó hace tiempo en esta revista
del uso conjunto de Transacciones con los TableUpdate sería el siguiente (se
están actualizando dos tablas (cabeceras y líneas):
BEGIN TRANSACTION
*******************************************
* Prueba si puede actualizar en tabla padre
*******************************************
IF TABLEUPDATE(0, .F., Orders)
*******************************************
* Tabla padre se actualizó correctamente, prueba
* si puede actualizar tabla hija
*******************************************
IF TABLEUPDATE(2, .F., OrdItems)
*******************************************
* Tabla hija se actualiza correctamente,
finaliza
* la transacción
*******************************************
END TRANSACTION
WAIT WINDOW "Cambios salvados" TIMEOUT 1
ELSE
*******************************************
* Problemas actualizando el hijo, pero el
padre
* ya está actualizado
*******************************************
= AERROR(aErrorArray)
CR = CHR(13)
cMsg = "Actualizando la tabla hija "+;
"ha ocurrido un error."+CR+CR+ ;
"Error: "+aErrorArray(1)+CR+CR+;
aErrorArray(2)
nType = MB_ICONINFORMATION+MB_OK
cTitle = "Problemas con actualización!"
*******************************************
* Descarta todos los cambios
*******************************************
ROLLBACK
=MESSAGEBOX(cMsg,nType,cTitle)
ENDIF
ELSE
*******************************************
* Problemas actualizando tabla padre
*******************************************
= AERROR(aErrorArray)
CR = CHR(13)
cMsg = "Actualizando tabla padre, "+;
"ha ocurrido un error."+CR+CR+ ;
"Error: "+aErrorArray(1)+CR+CR+;
aErrorArray(2)
nType = MB_ICONINFORMATION+MB_OK
cTitle = "Problemas con actualización!"
*******************************************
* Descarta los cambios
*******************************************
ROLLBACK
=MESSAGEBOX(cMsg,nType,cTitle)
ENDIF
Las transacciones sirven como otra forma de control sobre los datos.
En este caso, nos permite asegurar que una unidad de trabajo puede ser
procesada como un todo o retornar a la situación original sin ningún cambio.
Los cambios realizados mientras una transacción está pendiente no se
escriben a la tabla hasta que se ejecuta el comando END TRANSACTION y los
cambios se pueden descartar si se ejecuta el comando ROLLBACK. Si el
sistema se cae o el usuario sale se la aplicación con los cambios pendientes, el
buffer de transacción es descartado dejando la tabla en su situación original. Es
importante señalar que a diferencia de otros DBMS, FoxPro no guarda un log
físico de las transacciones, de forma que si el sistema se cae no existe forma
de recuperar esos cambios.
Ya que BEGIN TRANSACTION y END TRANSACTION usan el punto
de vista pesimista, es mejor reducir el espacio de tiempo que el programa está
sometido a estos comandos.
Se pueden establecer transacciones hasta con 5 niveles de
profundidad. Cuando una transacción termina, no vuelca los resultados a la
tabla sino al Buffer de la Transacción anterior.
De todos modos, las instrucciones anteriores sólo funcionan con tablas
y vistas locales pero no con datos remotos. Si quieres hacerlo en remoto
deberás usar SQLSetProp() para crear la transacción y luego usar
SQLCommit() y SQLRollback().
Las Transacciones en SQLServer
En SQL Server las Transacciones son algo diferentes pues se pueden
distinguir de dos tipos: implícitas o explícitas.
Las Transacciones Implícitas las crea el propio sistema y las explícitas
las escribes tú empezando con BEGIN TRANSACTION y acabando con
COMMIT o ROLLBACK
Las implícitas se crean incluso con una simple SELECT. Aunque no lo
veas, antes de hacer la SELECT, SQL Server habre una transacción y al final
la cierra. Esto tiene múltiples ventajas pues si sé que cada operación en la
Base de Datos me crea de forma automática una transacción, podría cerrarla
de forma manual. Por ejemplo, el siguiente Trigger rechazará cualquier
actualización de la tabla de productos que toque el precio (fíjate que no hay
inicio de transacción pero sí de cierre):
Create Trigger tcproductosUpdate
On productos
For Update
AS
IF Update(precio)
Begin
Rollback Transaction
Raiseerror 9999 “No puedes actualizar los precios”
End
Si tienes dos tablas relacionadas del tipo pedidos y líneas_de_pediodo y
quieres borrar un pedido entero con sus líneas, esto por defecto creará dos
transacciones. Para crear una sola transacción deberás establecer las
transacciones como manuales para o borrar los dos registros o no borrar
ninguno.
En las transacciones tenemos más diferencias con respecto a Fox y es
que mientras en Fox sólo puedes crear 5 niveles de profundidad en las
transacciones, aquí puedes crear ilimitados.
Otra diferencia importante es que en Fox necesitas tantos (End
Transaction ó Rollback) cuantas transacciones tengas, sin embargo en SQL
Server un error en una transacción en por ejemplo el tercer nivel de
profundidad te desmonta todas las operaciones de todos los niveles (de arriba
y de debajo de esa transaccion)
Cuando estás haciendo aplicaciones Cliente/Servidor desde Fox puedes
indicarle al Servidor que las transacciones las vas a manejar tú mediante la
instrucción :
SQLSETPROP(nHandle,’Transactions’,2)
Si usas las transacciones en manual es muy importante al acabar
acordarse de ponerlas en automático otra vez. Mira el siguiente código:
? SQLSetProp(gnhandle,’Transactions’,2) &&
Devuelve 1 si ha habido un éxito
? SQLExec (gnhandle,” Select count(*) from predidos
where cod_ped = 3”)
? [Link] && Devuelve 1 indicando que se ha
encontrado el pedido
? SQLExec (gnHandle, “delete pedidos where cod_ped =
3”)
? SQLExec (gnhandle,” Select count(*) from predidos
where cod_ped = 3”)
? [Link] && Devuelve 0 indicando que no se
ha encontrado el pedido
? SQLRollback (gnhandle)
? SQLExec (gnhandle,” Select count(*) from predidos
where cod_ped = 3”)
? [Link] && Devuelve 1 indicando que se ha
encontrado el pedido
? SQLSetProp(gnhandle,’Transactions’,1)
Como ves el equivalente del BEGIN TRANSACTION es el
SQLSETPROP(). En las líneas anteriores se podría meter un control de
devolución de parámetros para controlar que el valor devuelto por cada uno de
los SQLEXEC es el correcto. Podríamos escribir algo así:
? SQLSetProp(gnhandle,’Transactions’,2) &&
Devuelve 1 si ha habido un éxito
lnresultado = SQLExec (gnHandle, “delete pedidos
where cod_ped = 3”)
IF lnresultado <>1
SQLRollback(gnhandle)
Messagebox(“Algo ha ido mal”)
Return
ENDIF
lnresultado = SQLExec (gnhandle,” “delete
lineas_de_pedidos where cod_ped = 3”)
IF lnresultado <>1
SQLRollback(gnhandle)
Messagebox(“Algo ha ido mal”)
Return
ENDIF
SQLCommit()
? SQLSetProp(gnhandle,’Transactions’,1)
Messagebox(“Se ha borrado correctamente”)
No obstante, trabajar con con el SQLSetProp(gnhandle,’Transactions’,1) es
un poco oscurantista ya que desde Fox podemos trabajar directamente con las
transacciones explícitas del SQL Server:
El anterior código lo podríamos traducir a éste:
SQLEXEC(nHandle,’BEGIN TRANSACTION’)
Lnresultado1 = SQLExec (gnHandle, “delete pedidos
where cod_ped = 3”)
Lnresultado2 = SQLExec (gnhandle,” “delete
lineas_de_pedidos where cod_ped = 3”)
IF lnresultado1 = 1 and lnResultado2 = 1
SQLEXEC(nHandle,’IF @@TranCount > 0 COMMIT’)
ELSE
SQLEXEC(nHandle,’IF @@TranCount > 0 ROLLBACK’)
ENDIF
Como se puede ver este código es mucho más limpio y fácil de seguir.
La variable @@TranCount nos devuelve el número de transacción.
En SQL Server tenemos algunas otras opciones que nos pueden servir
de utilidad como la variable @@NESTLEVEL que nos indica en que nivel de
profundidad de las transacciones estamos metidos.
De todos modos creo que con estos ejemplos, hay suficiente.