Miércoles, 14 de Septiembre de 2005

Tablas con contadores en SQL

Varias personas me ha pedido en diversas ocasiones que les explique como crear una tabla con contadores desde SQL.

Supongamos que tenemos una tabla que va a tener estos tres contadores en una única fila Pedido, Albaran y Factura.

Creamos la tabla:

CREATE TABLE contadores (
pedido INT NOT NULL,
albaran INT NOT NULL,
factura INT NOT NULL )

Inicializamos los contadores (se toma cero para los valores iniciales):

INSERT INTO contadores VALUES ( 0, 0, 0 )

Obtener, por ejemplo, el valor del siguiente albarán:

1.- Bloqueamos la tabla para evitar que otro usuario acceda a la vez.
LOCK TABLES contadores WRITE

2.- Aumentamos el valor de albarán.
UPDATE contadores SET albaran = albaran + 1

3.- Obtenemos el nuevo valor.
SELECT albaran FROM contadores

4.- Quitamos el bloqueo.
UNLOCK TABLES

Hasta aqui la teoria, veamos ahora como se hace esto con Eagle 1 y MySQL:

(Usando funciones wrapeadas del cliente MySQL de la version 2.5 de Eagle 1)

// hCon contiene el handler de la conexion
// hRes contiene el handler de la consulta
// nAlbara recogera el valor del albaran correspondiente.

IF MSQuery( hCon, "LOCK TABLES contadores WRITE" )

...IF MSQuery( hCon, "UPDATE contadores SET albaran = albaran + 1" )

......IF MSQuery( hCon, "SELECT albaran FROM contadores" )

.........hRes := MSStoreResult( hCon )
.........nAlbaran := Val( MyReadRow( hRes )[1] )
.........MSQuery( "UNLOCK TABLES" )

......ELSE

.........Alert( MSError( hCon ) )

......END IF

...ELSE

......Alert( MSError( hCon ) )

...END IF

ELSE

...Alert( MSError( hCon ) )

END IF

De la misma forma se averiguaria el valor de cualquiera de los otros contadores.

Actualización:

Demo Xailer de contadores con MySQL

Ejemplo en Xailer disponible. Pedir por e-mail, gracias. (jasm en chochurro punto com).
Jose A. Suárez   |  Comentarios [6]   |  TrackBack (0)

Comentarios

Una pequeña optimización (si se me permite) sin tener que bloquear la tabla en MYSQL

UPDATE contadores SET albaran = LAST_INSERT_ID(albaran + 1)
SELECT LAST_INSERT_ID()

Para PostgreSQL y otras bases de datos utilizo secuencias que están hechas a propósito para este tema.

Comentado por Jorge perez a Miércoles, 14 de Septiembre de 2005

Jorge,

Eso funciona en el caso de que la columna sea autoincremental.

En este caso es una tabla con varias columnas y una unica fila que contiene los contadores de pedidos, albaranes y facturas.

Saludos,

Comentado por Jose A. Suárez a Miércoles, 14 de Septiembre de 2005

Hola Jose. No es necesario que la columna sea autoincremental. Es lo que yo uso para implementar contadores en MySQL y funciona perfectamente.

Comentado por Jorge perez a Miércoles, 14 de Septiembre de 2005

Jorge,

Funciona con esta corrección:

UPDATE contadores SET albaran = LAST_INSERT_ID(albaran + 1);
SELECT LAST_INSERT_ID(albaran) FROM contadores;

Gracias por el apunte.

Aunque es lo mismo que hacer:

UPDATE contadores SET albaran = albaran + 1;
SELECT albaran FROM contadores;

Por lo que es necesario bloquear la tabla para que no se desvirtúe el valor en caso de que entre un UPDATE desde otro usuario.

Supongo que con el LAST_INSERT_ID() ocurrira lo mismo, es decir, que sea necesario bloquear.


Saludos,

Comentado por Jose A. Suárez a Miércoles, 14 de Septiembre de 2005

Jose. Debe funcionar tal y como lo he descrito en mi post previo. Eso sí en un MySQL 4.1.

Adjunto está el enlace de la documentación de MYSQL que explica el truquito. En inglés claro.

http://dev.mysql.com/doc/mysql/en/innodb-locking-reads.html

Comentado por Jorge perez a Miércoles, 14 de Septiembre de 2005

Jorge,

No va ni en la 4.0.17 ni en lqa 4.0.23.

Voy a actualizar el servidor y a probar.

Comentado por Jose A. Suárez a Miércoles, 14 de Septiembre de 2005