lunes, 27 de febrero de 2012

MySQL y ONLY_FULL_GROUP_BY

Una de "arreglo una cosa y estropeo otra". MySQL, históricamente, se toma varias licencias frente a los estándares de SQL que pueden venir bien o no tan bien. Uno que particularmente no me gusta nada es la relajación de las columnas obligatorias en el GROUP BY.

Si no tocas nada de la configuración del servidor, cosas como esta se pueden hacer:

drop table if exists TT;
create table TT(a varchar(2), b varchar(1), c int, primary key (a));

insert into TT values ('a1','a',1),('a2','a',2),('a3','b',10);

select a,b,sum(c)
from TT
group by b;



absum(c)
a1a3
a3b10

Desde el estándar SQL, group by debería contener todas las columnas no calculadas que aparecen en select, es decir, group by a,b, independientemente de que esto tenga sentido o no. En realidad, todavía no entiendo qué se gana permitiendo esta sintáxis. Sí tendría algún sentido, por aquello de ahorrarnos algo de trabajo, si la columna "b" dependiera funcionalmente de la columna "a", de hecho algo de eso hay en el estándar SQL99, pero sigo sin verle mucha gracia.

De todas formas, como se trata de enseñar SQL y no las triquiñuelas de MySQL, a nuestros alumnos les obligamos a que no hagan estas cosas, es decir, la compilación de esta orden debería dar un error similar a "TT.a no está en GROUP BY". Para que se comporte así, MySQL permite configurar una variable global, sql_mode, en la que se puede establecer, entre otras cosas, ONLY_FULL_GROUP_BY, esto es, "compórtate como te dice el estándar"; y como hacen otros como Oracle o SQL Server, dicho sea de paso.

set session sql_mode='ONLY_FULL_GROUP_BY';

Esto obliga a cambiar la orden durante la sesión que tengamos abierta en MySQL. Si seguimos queriendo que el group by se haga por las dos columnas anteriores:

select a,b,sum(c)
from TT
group by a,b;


absum(c)
a1a1
a2a2
a3b10

Bueno, seguramente es que tampoco queríamos esto sino

select b,sum(c)
from TT
group by b;


bsum(c)
a3
b10

Que es en realidad para lo que vale el group by.

Resulta que lo pones todo contento, el sql_mode mencionado, y va y otras consultas que antes funcionaban ahora dejan de hacerlo.

select b
from TT
where c >= ALL (select c from TT);

La consulta anterior, estando sql_mode='ONLY_FULL_GROUP_BY', genera un error cuando no debería hacerlo. Di tú que la suerte es que para la mayoría de las cosas que preguntamos en clase hay una alternativa y además intuitiva:

select b
from TT
where c = (select max(c) from TT);


Pero no deja de ser molesto. Es un bug más o menos documentado que, hasta donde yo sé, se arrastra al menos hasta las versiones 5.1.x de MySQL. En las 5.5.x he comprobado que ya no. 

-----
Actualización: nos han actualizado a la versión 5.1.61 y ha desaparecido el bug.

No hay comentarios:

Publicar un comentario