ERROR 1055: ONLY_FULL_GROUP_BY
El error 1055 aparece en consultas con GROUP BY que seleccionan columnas que no están agrupadas ni dentro de una función de agregación. Es uno de los errores que más desconcierta, porque suele surgir en consultas que llevaban años funcionando y que de repente fallan tras actualizar MySQL, cambiar de servidor o migrar una aplicación. La consulta no ha cambiado: lo que ha cambiado es que MySQL ahora se niega a devolver un resultado que en realidad era ambiguo. En este artículo verás qué significa exactamente el mensaje, por qué MySQL lo aplica y cómo reescribir la consulta correctamente en cada caso, sin recurrir a desactivar el modo ONLY_FULL_GROUP_BY.
El mensaje de error
El mensaje es largo, pero cada parte aporta información útil. Supongamos que queremos el total gastado por cada cliente en la tabla pedidos, y además añadimos la columna fecha al SELECT:
SELECT cliente_id, fecha, SUM(total) AS gastado
FROM pedidos
GROUP BY cliente_id;ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'tienda_mysql.pedidos.fecha' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
El número tras Expression # indica la posición de la expresión problemática dentro de la lista del SELECT, empezando por 1. Aquí es la segunda, fecha. Después aparece la columna completa con el formato base_de_datos.tabla.columna, usando el nombre real de la tabla aunque en la consulta hayas usado un alias. El final del mensaje menciona el modo SQL responsable, only_full_group_by, que es la regla que la consulta incumple.
La misma comprobación se aplica a otras partes de la consulta. Si la columna no agrupada está en el ORDER BY, el mensaje cambia ligeramente y habla de Expression #1 of ORDER BY clause. La cláusula HAVING sigue la misma lógica: solo puede referirse a columnas agrupadas o a resultados de funciones de agregación.
Por qué MySQL rechaza la consulta
Para entender el error hay que pensar en lo que hace GROUP BY. La consulta anterior agrupa todos los pedidos de cada cliente en una sola fila. Si el cliente 7 tiene doce pedidos, el resultado tendrá una única fila para ese cliente, con la suma de los doce importes. Pero esos doce pedidos tienen doce fechas distintas, y la consulta pide mostrar una sola. ¿Cuál? La primera, la última, una cualquiera: la consulta no lo dice, así que el resultado no está definido.
Antiguamente MySQL resolvía esta ambigüedad eligiendo un valor cualquiera del grupo, normalmente el de la primera fila que leía. Durante años eso permitió escribir consultas "que funcionaban", pero el valor devuelto dependía del orden físico de las filas, de los índices usados o del plan de ejecución, y podía cambiar de una ejecución a otra sin aviso. Es el tipo de fallo más peligroso: no da error, simplemente devuelve datos incorrectos que parecen correctos.
El modo ONLY_FULL_GROUP_BY existe para impedirlo. Obliga a que cada columna del SELECT en una consulta agrupada cumpla una de estas condiciones: estar en el GROUP BY, estar dentro de una función de agregación como SUM, COUNT o MAX, o depender funcionalmente de las columnas agrupadas (lo veremos más abajo). Es el comportamiento que exige el estándar SQL y el que aplican otros motores como PostgreSQL.
Por qué aparece al actualizar MySQL
El modo ONLY_FULL_GROUP_BY está activado por defecto desde MySQL 5.7.5, y forma parte del sql_mode predeterminado en MySQL 8.0 y 8.4. En versiones anteriores estaba desactivado. Por eso el error suele aparecer en tres situaciones: al actualizar un servidor desde MySQL 5.6 o anterior, al migrar una aplicación antigua a un hosting nuevo con MySQL 8, o al pasar de MariaDB (que no lo activa por defecto) a MySQL.
Puedes comprobar el modo activo en tu sesión con esta consulta:
SELECT @@SESSION.sql_mode;En un MySQL 8 con la configuración por defecto verás algo como esto:
ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
Si tu consulta funciona en local y falla en producción, o al revés, compara el resultado de esta consulta en ambos entornos. Casi siempre la diferencia está en el sql_mode.
Cómo solucionarlo
La solución correcta depende de lo que realmente quieres obtener. El error te obliga a decidir qué valor quieres para esa columna, y cada decisión se traduce en una forma distinta de escribir la consulta.
Añadir la columna al GROUP BY
Si la columna forma parte de lo que quieres agrupar, la solución es añadirla al GROUP BY. Por ejemplo, si lo que buscas es el gasto de cada cliente en cada fecha, la agrupación debe incluir ambas columnas:
SELECT cliente_id, fecha, SUM(total) AS gastado
FROM pedidos
GROUP BY cliente_id, fecha;Ten en cuenta que esto cambia el significado del resultado: ya no obtienes una fila por cliente, sino una por cliente y fecha. Es la solución adecuada solo si ese es el resultado que buscas. Si añades columnas al GROUP BY únicamente para que desaparezca el error, puedes acabar con más filas de las esperadas. Puedes repasar cómo funciona la agrupación en el artículo sobre GROUP BY.
Usar una función de agregación
Si quieres una fila por cliente y necesitas un valor concreto de la columna, exprésalo con una función de agregación. Lo más habitual es querer la fecha del último pedido o la del primero, y para eso están MAX y MIN:
SELECT cliente_id,
MAX(fecha) AS ultimo_pedido,
SUM(total) AS gastado
FROM pedidos
GROUP BY cliente_id;Ahora la consulta es precisa: para cada cliente devuelve la fecha más reciente y el total gastado. No hay ambigüedad posible, y el resultado será siempre el mismo.
Existe también la función ANY_VALUE(), que le dice a MySQL explícitamente que te da igual qué valor del grupo devuelva. Úsala solo cuando sepas que todos los valores del grupo son iguales o cuando de verdad no importe cuál aparezca:
SELECT cliente_id,
ANY_VALUE(ciudad_envio) AS ciudad,
COUNT(*) AS pedidos
FROM pedidos
GROUP BY cliente_id;ANY_VALUE() reproduce el comportamiento antiguo, pero de forma explícita y solo en la columna donde lo indicas. Es útil para migrar consultas heredadas, aunque conviene no abusar de ella: si la usas en una columna cuyos valores sí varían dentro del grupo, vuelves a tener un resultado impredecible.
Agrupar por la clave primaria
Una situación muy común es agrupar para calcular un total por cliente y mostrar a la vez el nombre y el email del cliente. Si agrupas por una columna que no es clave, el error aparece. Pero si agrupas por la clave primaria de la tabla, MySQL sabe que todas las demás columnas de esa tabla tienen un único valor por grupo:
SELECT c.id, c.nombre, c.email, SUM(p.total) AS gastado
FROM clientes c
JOIN pedidos p ON p.cliente_id = c.id
GROUP BY c.id;Esta consulta funciona aunque nombre y email no estén en el GROUP BY. Es lo que el mensaje llama dependencia funcional: como c.id es la clave primaria de clientes, cada valor de id determina un único nombre y un único email, así que no hay ambigüedad. MySQL detecta esta relación con claves primarias y con columnas UNIQUE NOT NULL, lo que evita tener que repetir en el GROUP BY todas las columnas del cliente.
Si en cambio agrupas por p.cliente_id (la columna de la tabla pedidos), MySQL no siempre puede deducir la dependencia con clientes, y el error vuelve a aparecer. Agrupar por la clave primaria de la tabla cuyas columnas quieres mostrar es la forma más limpia de resolverlo.
Obtener la última fila de cada grupo
El caso real más frecuente detrás del error 1055 es este: quieres el último pedido de cada cliente con todos sus datos (fecha, importe, estado). Mucha gente lo intenta con un GROUP BY y un MAX(fecha), esperando que las demás columnas correspondan a ese último pedido:
SELECT cliente_id, MAX(fecha) AS fecha, total, estado
FROM pedidos
GROUP BY cliente_id;Incluso sin ONLY_FULL_GROUP_BY, esta consulta está mal: MAX(fecha) devuelve la fecha más reciente, pero total y estado pueden venir de cualquier otro pedido del cliente. El error de MySQL te está avisando de un fallo lógico real. La forma correcta en MySQL 8 es numerar los pedidos de cada cliente con la función de ventana ROW_NUMBER y quedarte con el primero:
SELECT cliente_id, fecha, total, estado
FROM (
SELECT cliente_id, fecha, total, estado,
ROW_NUMBER() OVER (PARTITION BY cliente_id ORDER BY fecha DESC) AS n
FROM pedidos
) AS ultimos
WHERE n = 1;Cada fila del resultado es un pedido real y completo: el más reciente de cada cliente. Si trabajas con una versión anterior a MySQL 8, puedes conseguir lo mismo con una subconsulta que calcule la fecha máxima por cliente y un JOIN contra ella. Esta técnica se explica con más detalle en el artículo sobre cómo seleccionar los N primeros de cada grupo.
Desactivar ONLY_FULL_GROUP_BY: por qué no es la solución
Si buscas este error en internet, la respuesta más repetida es quitar ONLY_FULL_GROUP_BY del sql_mode. Funciona, en el sentido de que el error desaparece, pero no arregla nada: la consulta sigue devolviendo valores arbitrarios, solo que ahora sin avisar. Es cambiar un error visible por datos incorrectos invisibles.
Aun así, conviene saber cómo se hace, porque a veces es necesario de forma temporal para mantener funcionando una aplicación heredada mientras se corrigen sus consultas. Para la sesión actual se puede eliminar el modo de la lista sin tocar los demás:
SET SESSION sql_mode = (SELECT REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY,', ''));Con SET GLOBAL el cambio afecta a las conexiones nuevas, pero se pierde al reiniciar el servidor. Para hacerlo permanente hay que definir el sql_mode completo en el archivo de configuración, dentro de la sección [mysqld], listando los modos que sí quieres conservar:
[mysqld]
sql_mode = STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTIONEl riesgo de hacerlo a nivel global es que afecta a todas las bases de datos y aplicaciones del servidor, incluidas las nuevas, que a partir de entonces podrán escribir consultas ambiguas sin enterarse. Si no queda más remedio, es preferible limitarlo a la sesión de la aplicación que lo necesita. Puedes ver cómo funcionan los ámbitos de estas variables en el artículo sobre variables de sesión.
En Laravel, el modo SQL lo controla la conexión definida en config/database.php. Con la opción strict a true, que es el valor por defecto, Laravel activa un conjunto de modos que incluye ONLY_FULL_GROUP_BY. La opción modes de esa misma conexión permite indicar la lista exacta. Poner strict a false hace desaparecer el error, pero también desactiva otras comprobaciones útiles del modo estricto, así que tiene los mismos inconvenientes que desactivarlo en el servidor.
Cómo evitar el error
La mejor prevención es escribir las consultas agrupadas pensando en cada columna del SELECT. Antes de añadir una columna a una consulta con GROUP BY, pregúntate si tiene un único valor por grupo. Si la respuesta es sí porque forma parte de la agrupación o depende de la clave primaria, puedes incluirla tal cual. Si no, decide qué valor quieres (el máximo, el mínimo, la suma, el más reciente) y exprésalo con la función adecuada.
También ayuda desarrollar con la misma configuración que producción. Si tu entorno local tiene ONLY_FULL_GROUP_BY desactivado y producción lo tiene activo, descubrirás los errores al desplegar. Mantener el modo activado en todos los entornos hace que los problemas salgan mientras escribes la consulta, que es cuando es más fácil corregirlos.
Resumen
El ERROR 1055 indica que una consulta agrupada selecciona una columna que puede tener varios valores dentro de cada grupo, y MySQL no sabe cuál devolver. Aparece desde MySQL 5.7.5 porque ONLY_FULL_GROUP_BY está activo por defecto, y suele destapar consultas que llevaban tiempo devolviendo datos arbitrarios. Para resolverlo, añade la columna al GROUP BY si forma parte de la agrupación, usa una función de agregación como MAX o MIN si quieres un valor concreto, agrupa por la clave primaria para mostrar columnas de la misma tabla, o usa ROW_NUMBER() cuando necesitas la fila completa más reciente de cada grupo. Desactivar el modo solo oculta el problema. Si la consulta falla por una columna mal escrita en lugar de por la agrupación, el error será otro, el ERROR 1054 Unknown column.
Escrito por Eduardo Lázaro
