El bloqueo en SQL es bastante mas complejo de lo que es en VFP. Basicamente, SQL maneja automaticamene los bloqueos, los cuales generalmente se crean al momento de actualizar una tabla y se liberan luego que los cambios son confirmados, bien sea implicitamente o explicitamente mediante un COMMIT.
SQL manejo multiples tipos de bloqueos, pero los mas comunes son los bloqueos por registro, pagina y tabla, Un bloqueo por registro, como su nombre lo indica, solo afecta a un registro particular en una tabla dada; por su parte, un bloqueo por pagina afecta al registro modificado y a todos los demas registros almacenados en la misma pagina fisica. Finalmente, un bloqueo de tabla afecta a todos los registros de la tabla modificada.
El tipo de bloqueo aplicado en un caso particular depende de multiples factores, ligados estrechamente a la clausula WHERE indicada y a los indices disponibles. Como regla general, cuando se hace un UPDATE o un DELETE sobre una tabla usando unicamente su primary key como WHERE, ej, DELETE FROM clientes WHERE codigo = ?cCodigo, SQL bloqueara solo el registro afectado.
En constraste, si la clausula WHERE implica mas de una columna y existe un indice definido para cada una de esas columnas, muy probablemente SQL aplicaa un bloqueo de pagina, pero si no existiera un indice creado para una o mas de las columnas mencionadas en el WHERE de un UPDATE o DELETE, entonces SQL aplicara un bloqueo a la tabla completa.
Que pasa si un usuario intenta actualizar un registro que esta bloqueado, bien sea especificamente, contenido en una pagina que esta bloqueada o en una tabla que esta bloqueada? SQL esperara el tiempo indicado en su configuracion actual y luego de ese tiempo generara un error por bloqueo.
Hay alguna forma sencilla de validar si un registro especifico esta bloqueado? lamentablemente en SQL no contamos con un equivalente para la funcion RLOCK() de VFP, asi que la unica forma de saber si un registro esta bloqueado o no es intentar actualizarlo y ver si se genera un error, lo cual no es nada practico.
Que cosas podemos hacer para minimizar el alcance de un bloqueo y el tiempo que este permanece activo?
a) Utilizar transacciones atomicas que duren lo menos posible. En especial, asegurarnos que no se intoducen wait-states en el medio de una transaccion, como por ejemplo mostrar un dialogo de confirmacion al usuario luyego de haber iniciado una transaccion.
b) Intentar siempre que sea posible el primary key al hacer un UPDATE o DELETE, ya que esto minimiza los bloqueos por pagina
c) Asegurarnos que existe un indice para todas las columnas mencionads en el WHERE de un UPDATE o DELETE, y evitar el uso de expresiones compuestas en los WHERE, tales como WHERE campo1 + campo2 = valor.
d) Utilizar el table-hint WITH (ROWLOCK) para indicar a la BD que force el uso de bloqueos de registro al hacer un UPDATE o DELETE. Esto es util para los casos en los que necesitamos actualizar o eliminar VARIOS registros en lugar de uno especifico, ej:
DELETE FROM clientes WITH (ROWLOCK) WHERE status = 'ANULADO'
Esto garantizara que SQL bloqueara exlusivamente los registros afectados en lugar de usar un bloqueo de pagina (lo que habria bloqueado los registros afectados y todos los demas registros cercanos a ellos). Sin embargo, hay que tener en cuenta que el uso de este table-hint no es recomendable para UPDATES o DELETEs que afectaran miles de registros, pues los bloqueos de registro consumen recursos del servidor y desde ese punto de vista es mucho mas eficiente tener unos cuantas decenas de bloqueos de pagina a tener miles de bloqueos de registro.
Saludos
Victor Espina