Transacciones en VFP y SQL Server
Por Alberto Rodríguez transac.prg
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.
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”)
? sqlresult.ext &&
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”)
? sqlresult.ext &&
Devuelve 0 indicando que no se ha encontrado el pedido
? SQLRollback (gnhandle)
? SQLExec (gnhandle,” Select count(*) from predidos where
cod_ped = 3”)
? sqlresult.ext &&
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.
FoxPress – Febrero de 2001
© 2001 FoxPress. All rights reserved