La première chose à préciser est que SQL n'est pas MySQL.
En SQL standard, il n'est pas permis de regrouper par un sous-ensemble des champs non agrégés. La raison est très simple. Supposons que j'exécute cette requête :
SELECT color, owner_name, COUNT(*) FROM cars
GROUP BY color
Cette requête n'aurait aucun sens. Même essayer de l'expliquer serait impossible. Bien sûr, il s'agit de sélectionner des couleurs et de compter le nombre de voitures par couleur. Cependant, il ajoute également le owner_name
champ et il peut y avoir plusieurs propriétaires pour une couleur donnée, comme c'est le cas du White
Couleur. Donc, s'il peut y avoir plusieurs owner_name
valeurs pour une seule color
qui se trouve être le seul champ du GROUP BY
clause... alors quel owner_name
sera retourné ?
S'il est nécessaire de renvoyer un owner_name
alors une sorte de critère devrait être ajouté pour n'en sélectionner qu'un seul, par exemple, le premier par ordre alphabétique, qui dans ce cas serait John
. Ce critère entraînerait l'ajout d'une fonction d'agrégation MIN(owner_name)
puis la requête aura à nouveau un sens car elle regroupera au moins tous les champs non agrégés dans l'instruction select.
Comme vous pouvez le voir, il existe une raison claire et pratique pour laquelle le SQL standard est inflexible dans le regroupement. Si ce n'était pas le cas, vous pourriez être confronté à des situations délicates dans lesquelles la valeur d'une colonne serait imprévisible, et ce n'est pas un joli mot, en particulier si la requête en cours d'exécution vous montre les transactions de votre compte bancaire.
Cela dit, pourquoi MySQL autoriserait-il des requêtes qui pourraient ne pas avoir de sens ? Et pire encore, l'erreur dans la requête ci-dessus pourrait être simplement détectée syntaxiquement ! La réponse courte est :la performance. La réponse longue est qu'il existe certaines situations dans lesquelles, sur la base des relations de données, l'obtention d'une valeur imprévisible du groupe se traduira par une valeur prévisible.
Si vous ne l'avez pas encore compris, la seule façon de prédire la valeur que vous obtiendrez en prenant un élément imprévisible d'un groupe sera si tous les éléments du groupe sont identiques. Un exemple clair de cette situation se trouve dans l'exemple de requête de votre même question. Regardez comment owner_id
et owner_name
rapporte dans le tableau. Il est clair qu'étant donné n'importe quel owner_id
, par exemple. 2
, vous ne pouvez avoir qu'un seul owner_name
distinct . Même si vous avez plusieurs lignes, en en choisissant une, vous obtiendrez Mike
comme résultat. Dans le jargon formel de la base de données, cela peut être expliqué comme owner_id
détermine fonctionnellement owner_name
.
Examinons de plus près cette requête MySQL entièrement fonctionnelle :
SELECT owner_id, owner_name, COUNT(*) total FROM cars
GROUP BY owner_id
Étant donné n'importe quel owner_id
cela renverrait le même owner_name
, donc en l'ajoutant au GROUP BY
La clause n'entraînera pas le retour de plusieurs lignes. Même en ajoutant une fonction agrégée MAX(owner_name)
n'entraînera pas moins de lignes renvoyées. Les données résultantes seront exactement les mêmes. Dans les deux cas, la requête serait immédiatement transformée en une requête SQL standard légale car au moins tous les champs non agrégés seraient regroupés par. Il existe donc 3 approches pour obtenir les mêmes résultats.
Cependant, comme je l'ai mentionné précédemment, ce regroupement non standard présente un avantage en termes de performances. Vous pouvez vérifier ce lien si sous-estimé dans lequel ceci est expliqué pour plus de détails mais je vais citer la partie la plus importante :
Une chose qui mérite d'être mentionnée est que les résultats ne sont pas nécessairement faux mais plutôt indéterminé . En d'autres termes, obtenir les résultats attendus ne signifie pas que vous avez écrit la bonne requête. Écrire la bonne requête vous donnera toujours les résultats attendus.
Comme vous pouvez le voir, il peut être intéressant d'appliquer cette extension MySQL au GROUP BY
clause. Quoi qu'il en soit, si ce n'est pas encore clair à 100 %, il existe une règle empirique qui garantira que votre regroupement sera toujours correct :Toujours grouper, au moins, par tous les champs non agrégés dans la clause select . Vous perdez peut-être quelques cycles CPU dans certaines situations, mais c'est mieux que de renvoyer indéterminé résultats. Si vous êtes toujours terrifié à l'idée de ne pas regrouper correctement, modifiez le ONLY_FULL_GROUP_BY
Le mode SQL pourrait être un dernier recours :)
Que votre regroupement soit correct et performant... ou du moins correct.