VictorCasajuana wrote:armando.lagunas wrote:Estimado:
lo que yo utilizo en SQL es un campo lógico (0-1) como una bandera, terminal A: toma el registro y le cambio el estado a ese campo inmediatamente a 0 (ocupado) y cuando termino los cambios realizados en el registro le devuelvo el estado 1 (Activo), entonces en la terminal B verifica el estado de este campo antes de hacer cualquier cosa en el registro.
lo otro es realizar procedimiento almacenado en la base de datos estableciendo SET TRANSACTION ISOLATION LEVEL READ COMMITTED que es lo que permite bloquear temporalmente registros en una base de datos SQL
busca en internet como utilizarlo hay varios LEVEL para analizar y utilizar
Espero que te sirva
Saludos
Hola Armando.
Este tema siempre me ha intrigado y a veces me pongo a buscar
info pero nunca llego a buen puerto. En tu caso lo tienes bien controlado ya que modificas la tabla. No me gusta mucho tener que modificar la estructura de datos para este fin pero se podría aceptar como solución. Pero me surge una duda, si el terminal A toma el registro, cambias la bandera a 1 y se cierra el programa sin pasar por el proceso de guardar la ficha, que haces? por ejemplo se puede apagar el ordenador, error de ejecución, etc...
aquí hay una buena charla que tuvimos hace meses al respecto:
https://forum.modharbour.app/viewtopic.php?f=25&t=259
Hola Victor, saludos.
Veo que el tema es recurrente, por lo que me tome el tiempo para hacer dos ejemplos sencillos que a mi me han funcionado muy bien. Jamas he tenido problema con mis sistemas en este aspecto.
Lo primero es aclarar que toda la magia la hace la instruccion
SELECT.. FOR UPDATE... y esto significa 3 cosas:
1.- Siempre hay que hacer un
SELECT... FOR UPDATE previo al
UPDATE ... SET eso es lo que nos va a bloquear el registro seleccionado.
sin permitir que nadie mas tenga acceso a el.
2.- Para que
SELECT... FOR UPDATE funcione debemos tener el
AUTOCOMMIT en 0 (OFF o FALSE) segun la version de mysql o mariadb.
si no se desea tener configurado de manera permanente, se puede activar o desactivar mientras hacemos la operacion.
3.- En caso de no querer usar el AUTOCOMMIT, la otra opcion mas comoda y tambien valida, es usar una transaccion. El caso o gusto particular de cada quien indicara
cual sistema usar, si
AUTOCOMMIT o
TRANSACCION.
Aca dejo los ejemplos:
USANDO AUTOCOMMIT- Code: Select all Expand view
# include FIVEWIN.CH
Function Fnct_Prueba()
Local oQuery, cQuery, cTabla
Local nNuevoNumero
_oSqlConex:=TDolphinSrv():New( _cHost, _cUser, _cPasswordUser,,, _cDataBase,bSQLCnxErr() ) // mis variables de conexion son publicas.
// cada quien debe manejar esta parte
if _oSqlConex:lError // segun su forma de trabajar.
_oSqlConex:End()
return .f.
endif
cQuery := "SET AUTOCOMMIT=0 ; " // colocamos el AUTOCOMMIT temporalmente en 0 (desactivado)
oQuery := _oSqlConex:Execute( cQuery ) // de esta forma AUTOCOMMIT funciona solo con la sesion actual
// en caso de que no este configurado por defecto
cTabla := _cPrefijo+'_docventatemp'
cQuery := ''
cQuery := "SELECT numerotemp_documento FROM "+ cTabla +" FOR UPDATE ; " // hacemos el SELECT... FOR UPDATE para bloquear la fila de
oQuery := _oSqlConex:Query( cQuery ) // la tabla
nNuevoNumero:=oQuery:numerotemp_documento+1 // aumentamos en 1 el contador consecutivo
cQuery :=''
cQuery :="Update "+cTabla+" Set "
cQuery +="numerotemp_documento:="+alltrim(str(nNuevoNumero))+" ; " // actualizamos la tabla
_oSqlConex:Execute( cQuery )
//msginfo('Stop') // activar si se desea chequear la tabla con phpmyadmin, heidi, navicat, etc. durante el proceso.
cQuery:="commit ; " // hacemos commit para actualizar la tabla.
oQuery:= _oSqlConex:Execute( cQuery )
cQuery := "SET AUTOCOMMIT=1 ; " // Colocamos el autocommit de nuevo a su estado activo
oQuery := _oSqlConex:Execute( cQuery )
_oSqlConex:End() // cerramos la conexion
return nNuevoNumero // nNuevoNumero es el numero asignado a la nueva factura que estamos emitiendo ...
Usando Transacciones- Code: Select all Expand view
# include FIVEWIN.CH
Function Fnct_Prueba()
Local oQuery, cQuery, cTabla
Local nNuevoNumero
_oSqlConex:=TDolphinSrv():New( _cHost, _cUser, _cPasswordUser,,, _cDataBase,bSQLCnxErr() ) // mis variables de conexion son publicas.
// cada quien debe manejar esta parte
if _oSqlConex:lError // segun su forma de trabajar.
_oSqlConex:End()
return .f.
endif
_oSqlConex:BeginTransaction() // comenzamos la transaccion
cTabla := _cPrefijo+'_docventatemp'
cQuery := ''
cQuery := "SELECT numerotemp_documento FROM "+ cTabla +" FOR UPDATE ; " // hacemos el SELECT... FOR UPDATE para bloquear la fila de
oQuery := _oSqlConex:Query( cQuery ) // la tabla
nNuevoNumero:=oQuery:numerotemp_documento+1 // aumentamos en 1 el contador consecutivo
cQuery :=''
cQuery :="Update "+cTabla+" Set "
cQuery +="numerotemp_documento:="+alltrim(str(nNuevoNumero))+" ; " // actualizamos la tabla
_oSqlConex:Execute( cQuery )
//msginfo('Stop') // activar si se desea chequear durante el proceso.
_oSqlConex:CommitTransaction()
_oSqlConex:End() // cerramos la conexion
return nNuevoNumero // nNuevoNumero es el numero asignado a la nueva factura que estamos emitiendo ...
En ambos casos, el registro se libera al ejecutarse el
COMMIT.
Esto es solo una idea de como lo hago, cada quien puede adaptarlo a su modo.
Espero haber ayudado. Quizas algun colega le consiga fallas o pueda sugerir mejoras. Todo es bienvenido. Pero como comento, jamas he tenido problemas con esta manera de obtener consecutivos sin que se repitan, no importa el numero de estaciones que accesen al mismo tiempo la tabla.