En ciertas circunstancias, no es conveniente una lectura
consistente. Por ejemplo, se podría desear agregar una fila en
la tabla hijo
, y estar seguro de que dicha
fila tiene una fila padre en la tabla padre
.
El siguiente ejemplo muestra cómo implementar integridad
referencial en el código de la aplicación.
Suponiendo que se utiliza una lectura consistente para leer la
tabla padre
y efectivamente puede verse el
registro padre para la fila hijo que se agregará, ¿puede
agregarse en forma segura la fila hijo dentro de la tabla
hijo
? No, porque puede haber ocurrido que
entretanto otro usuario haya borrado el registro padre de la
tabla padre
, sin que se tenga conocimiento de
ello.
La solución es llevar a cabo el SELECT
en un
modo con bloqueo, utilizando LOCK IN SHARE
MODE
:
SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;
Realizar una lectura en modo compartido (share mode) significa
que se leen los últimos datos disponibles, y se establece un
bloqueo en modo compartido en los registros que se leen. Un
bloqueo en modo compartido evita que otros actualicen o eliminen
la fila que se ha leido. Además, si los datos más actualizados
pertenecen a una transacción todavía no confirmada de otra
conexión, se espera hasta que la transacción se confirme.
Luego de ver que la mencionada consulta devuelve el registro
padre 'Jones'
, se puede agregar con seguridad
el registro hijo en la tabla hijo
y confirmar
la transacción.
Otro ejemplo: se tiene un campo contador, entero, en una tabla
llamada child_codes
que se emplea para
asignar un identificador único a cada registro hijo agregado a
la tabla hijo
. Obviamente, utilizar una
lectura consistente o una lectura en modo compartido para leer
el valor actual del contador no es una buena idea, puesto que
dos usuarios de la base de datos pueden ver el mismo valor del
contador, y agregar registros hijos con el mismo identificador,
lo cual generaría un error de clave duplicada.
En este caso, LOCK IN SHARE MODE
no es una
buena solución porque si dos usuarios leen el contador al mismo
tiempo, al menos uno terminará en un deadlock cuando intente
actualizar el contador.
En este caso, hay dos buenas formas de implementar la lectura e
incremento del contador: (1), actualizar el contador en un
incremento de 1 y sólo después leerlo, o (2) leer primero el
contador estableciendo un bloqueo FOR UPDATE
,
e incrementándolo luego. La última puede ser implementada como
sigue:
SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes SET counter_field = counter_field + 1;
Una sentencia SELECT ... FOR UPDATE
lee el
dato más actualizado disponible, estableciendo bloqueos
exclusivos sobre cada fila leída. Es decir, el mismo bloqueo
que haría UPDATE
.
Nótese que el anterior es un sencillo ejemplo de cómo funciona
SELECT ... FOR UPDATE
. En MySQL, la tarea
específica para generar un identificador único en realidad
puede realizarse utilizando un sólo acceso a la tabla:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1); SELECT LAST_INSERT_ID();
La sentencia SELECT
simplemente recupera la
información del identificador (relativa a la conexión actual).
No accede ninguna tabla.
Ésta es una traducción del manual de referencia de MySQL, que puede encontrarse en dev.mysql.com. El manual de referencia original de MySQL está escrito en inglés, y esta traducción no necesariamente está tan actualizada como la versión original. Para cualquier sugerencia sobre la traducción y para señalar errores de cualquier tipo, no dude en dirigirse a mysql-es@vespito.com.