sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo GROUP BY correctamente en MySQL?

Lo primero que hay que aclarar es que SQL no es MySQL.

En SQL estándar, no se permite agrupar por un subconjunto de campos no agregados. La razón es muy sencilla. Supongamos que estoy ejecutando esta consulta:

SELECT color, owner_name, COUNT(*) FROM cars
GROUP BY color

Esa consulta no tendría ningún sentido. Incluso tratar de explicarlo sería imposible. Seguro que es seleccionando colores y contando la cantidad de coches por color. Sin embargo, también está agregando el owner_name campo y puede haber muchos propietarios para un color dado, como es el caso del White color. Entonces, si puede haber muchos owner_name valores para un solo color que resulta ser el único campo en GROUP BY cláusula... entonces cuál owner_name será devuelto?

Si es necesario devolver un owner_name entonces se debe agregar algún tipo de criterio para seleccionar solo uno de ellos, por ejemplo, el primero en orden alfabético, que en este caso sería John . Ese criterio daría como resultado la adición de una función agregada MIN(owner_name) y luego la consulta volverá a tener sentido ya que se agrupará, al menos, por todos los campos no agregados en la declaración de selección.

Como puede ver, existe una razón clara y práctica para que SQL estándar sea inflexible en la agrupación. Si no fuera así, podría enfrentar situaciones incómodas en las que el valor de una columna será impredecible, y esa no es una palabra agradable, especialmente si la consulta que se ejecuta muestra las transacciones de su cuenta bancaria.

Habiendo dicho eso, ¿por qué MySQL permitiría consultas que podrían no tener sentido? Y lo que es peor, ¡el error en la consulta anterior podría detectarse sintácticamente! La respuesta corta es:rendimiento. La respuesta larga es que hay ciertas situaciones en las que, según las relaciones de datos, obtener un valor impredecible del grupo dará como resultado un valor predecible.

Si aún no lo ha descubierto, la única forma en que puede predecir el valor que obtendrá al tomar un elemento impredecible de un grupo será si todos los elementos del grupo son iguales. Un claro ejemplo de esta situación está en la consulta de muestra en su misma pregunta. Mira cómo owner_id y owner_name relaciona en la tabla. Está claro que dado cualquier owner_id , p.ej. 2 , solo puede tener un owner_name distinto . Incluso teniendo muchas filas, al elegir cualquiera, obtendrás Mike como el resultado. En la jerga formal de la base de datos, esto se puede explicar como owner_id determina funcionalmente owner_name .

Echemos un vistazo más de cerca a esa consulta MySQL completamente funcional:

SELECT owner_id, owner_name, COUNT(*) total FROM cars
GROUP BY owner_id

Dado cualquier owner_id esto devolvería el mismo owner_name , así que agréguelo al GROUP BY cláusula no dará como resultado más filas devueltas. Incluso agregando una función agregada MAX(owner_name) no dará como resultado menos filas devueltas. Los datos resultantes serán exactamente los mismos. En ambos casos, la consulta se convertiría inmediatamente en una consulta SQL estándar legal, ya que al menos todos los campos no agregados se agruparían por. Así que hay 3 enfoques para obtener los mismos resultados.

Sin embargo, como mencioné antes, esta agrupación no estándar tiene una ventaja de rendimiento. Puede consultar este enlace tan infravalorado en el que se explica esto con más detalle pero voy a citar la parte más importante:

Una cosa que vale la pena mencionar es que los resultados no son necesariamente incorrectos sino más bien indeterminado . En otras palabras, obtener los resultados esperados no significa que haya escrito la consulta correcta. Escribir la consulta correcta siempre le dará los resultados esperados.

Como puede ver, podría valer la pena aplicar esta extensión de MySQL al GROUP BY cláusula. De todos modos, si esto aún no está 100% claro, existe una regla general que garantizará que su agrupación siempre sea correcta:Agrupe siempre, al menos, por todos los campos no agregados en la cláusula de selección . Es posible que esté desperdiciando algunos ciclos de CPU en ciertas situaciones, pero es mejor que devolver indeterminado resultados. Si todavía está aterrorizado por no agrupar correctamente, cambie el ONLY_FULL_GROUP_BY El modo SQL podría ser el último recurso :)

Que tu agrupación sea correcta y eficiente... o al menos correcta.