6 de Octubre, 2005

Estrategia: ir hacia adelante

El Software Libre siempre está en desarrollo, aunque esté acabado, hay muy pocos productos que se estanquen en versiones finales.

Podemos decir: si esta versión que uso era buena cuando la instalé, ¿por qué cambiar? Pues puedo aportar mi expericia personal, y uno de los paquetes que más problemas me han causado por trabajar con versiones poco actuales es MySQL.

La gente se suele sorprender, ¿MySQL no siempre ha tenido subconsultas? Pues no señores, antes de la versión 4.1 no hay. Y vaya si complica las cosas :(.

Esta semana mismo he tenido que hacer una consulta algo más compleja de la cuenta, y me he visto atrapado en un MySQL 3.23 que venía en Debian Woody, y sin posibilidades de actualizar.

Voy a simplificar el problema, creo que no es necesario que describa un 20% de la aplicación para entender qué pasaba.

Tengo una serie de etiquetas a las que realizan n lecturas y quiero obtener por cada identificador de etiqueta el tipo de la última lectura. Bien, sencillo... SQL tiene suficientes recursos para eso:

select b.idEtiqueta, a.tipo from tblLectura as a, tblEtiqueta as b
where b.idNota=$nota and b.id=a.idEtiqueta and
a.fecha=
	(select max(fecha) from tblLectura as a2, tblEtiqueta as b2
	where b2.idNota=$nota and b2.id=a2.idEtiquetab)
order by b.idEtiqueta asc

Fácil, fácil. Hacemos una subconsulta que nos garantice obtener la última lectura por etiqueta, y asunto solucionado. Pero, ¿y si no podemos realizar subconsultas? Bienvenido al infierno :P.

El primer intento es agrupar por etiqueta:

select b.idEtiqueta, a.tipo from tblLectura as a, tblEtiqueta as b
where b.idNota=$nota and b.id=a.idEtiqueta
group by b.idEtiqueta
order by b.idEtiqueta asc

Buen intento, aunque lamentablemente nadie nos garantiza que al agrupar así el tipo que obtengamos sea el que corresponde a la última lectura, ni todo lo contrario. Tenemos un resultado incierto :D.

Afortunadamente la documentación de MySQL es de primera: The Rows Holding the Group-wise Maximum of a Certain Field.

La solución propuesta para MySQL 4.1 es exáctamente la más lógica (claro, con subconsultas :S), y la que nos da para versiones anteriores consiste en hacer el trabajo en pasos.

Para ello crea una tabla temporal. Este tipo de tablas tienen la particularidad de solo existir para la conexión que las crea y, además, por el tiempo que dura esa conexión.

El resultado es tal que:

-- tabla temporal --
create temporary table tmp(idEtiqueta int, fecha datetime);
-- bloqueamos las tablas implicadas para solo lectura --
lock tables tblLectura read, tblEtiqueta read;

-- insertamos los máximos (parece un subquery, pero no lo es :D) --
insert into tmp select idEtiqueta, max(fecha)
from tblLectura, tblEtiqueta
where idNota=$nota and tblEtiqueta.id=idEtiqueta
group by idEtiqueta;

-- hacemos 'la pregunta' --				
select b.idEtiqueta, a.tipo from tblLectura as a, tblEtiqueta as b, tmp as c
where b.idNota=$nota and b.id=a.idEtiqueta and
a.fecha=c.fecha and a.idEtiqueta=c.idEtiqueta
order by b.idEtiqueta asc

-- quitamos el bloqueo --
unlock tables;
-- limpiamos la casa --
drop table tmp;

Sí, dan escalofríos, aunque en este caso es la solución más cómoda. En otros se puede recurrir a programación del lenguaje donde empleamos SQL para suplir la falta de subconsultas (como generando listas de claves primarias para usarlas en un where id in($lista)).

Desde luego MySQL avanza con pasos firmes. La versión 5.0, de la que saldrá pronto versión para producción, nos trae cosas tan jugosas como:

  • Vistas
  • Procedimientos y funciones almacenadas
  • Disparadores
  • Cursores en servidor

Puede ser un poco lioso saber qué funcionalidades tiene o no la versión con la que trabajamos, aunque la primera versión de MySQL 4.1.x apta para producción data ya de Octubre de 2004, con lo que esta situación que he tenido que sufrir cada vez debe ser menos frecuente (en este servidor aún está MySQL 4.0.x, sin subconsultas).

De los problemas de no tener transacciones ni integridad referencial ya hablamos otro día ;).

Anotación por Juan J. Martínez, clasificada en: sql, mysql.

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.

Algunas anotaciones relacionadas: