2009-06-21 7 views
35

Je suis en train d'ajouter des fonctionnalités à une application pré-existante et je suis tombé sur une vue MySQL quelque chose comme ceci:MySQL - Sélection d'une colonne non dans le groupe par

SELECT 
    AVG(table_name.col1), 
    AVG(table_name.col2), 
    AVG(table_name.col3), 
    table_name.personID, 
    table_name.col4 
FROM table_name 
GROUP BY table_name.personID; 

OK donc il y a quelques fonctions d'agrégation. Vous pouvez sélectionner personID parce que vous le groupez. Mais il sélectionne également une colonne qui n'est pas dans une fonction d'agrégat et qui ne fait pas partie de la clause GROUP BY. Comment est-ce possible??? Choisit-il simplement une valeur aléatoire parce que les valeurs ne sont pas uniques par groupe?

D'où je viens (MSSQL Server), c'est une erreur. Quelqu'un peut-il m'expliquer ce comportement et pourquoi est-il autorisé dans MySQL?

Répondre

40

Il est vrai que cette fonctionnalité autorise certaines requêtes ambiguës et renvoie silencieusement un jeu de résultats avec une valeur arbitraire choisie dans cette colonne. En pratique, il tend à être la valeur de la rangée dans le groupe qui est physiquement stockée en premier.

Ces requêtes ne sont pas ambiguës si vous ne choisissez que les colonnes fonctionnellement dépendantes de la colonne (s) dans les critères GROUP BY. En d'autres termes, s'il ne peut y avoir qu'une seule valeur distincte de la colonne "ambiguë" par valeur qui définit le groupe, il n'y a pas de problème. Cette requête serait illégale dans Microsoft SQL Server (et ANSI SQL), même si elle ne peut pas aboutir logiquement à l'ambiguïté:

SELECT AVG(table1.col1), table1.personID, persons.col4 
FROM table1 JOIN persons ON (table1.personID = persons.id) 
GROUP BY table1.personID; 

En outre, MySQL a un mode SQL pour qu'il se comporte selon la norme: ONLY_FULL_GROUP_BY

FWIW, SQLite autorise également ces clauses GROUP BY ambiguës, mais il choisit la valeur de la dernière ligne dans le groupe.


au moins dans la version de mon test. Qu'est-ce que cela signifie d'être arbitraire est que MySQL ou SQLite pourrait changer leur implémentation dans le futur, et avoir un comportement différent. Vous ne devriez donc pas compter sur le comportement de rester comme il est actuellement dans des cas ambigus comme celui-ci. Il est préférable de réécrire vos requêtes pour qu'elles soient déterministes et non ambiguës. C'est pourquoi MySQL 5.7 active désormais ONLY_FULL_GROUP_BY par défaut.

+3

Je voudrais faire remarquer que ce n'est pas tout à fait vrai. A partir de ANSI SQL-99, les champs sélectionnés doivent être des agrégats, dépendent fonctionnellement de la clause group by. Donc, la sélection de user_name lors du regroupement par user_id est totalement correcte. SQL Server et Oracle ne le respectent pas, car ils ne permettent pas de sélectionner user_name lorsque seul user_id figure dans le groupe par liste; et MySQL n'est pas conforme, car il ne vérifie pas si chaque colonne sélectionnée dépend vraiment de user_id. –

+0

@ThorstenKettner, merci, vous avez raison. MySQL 5.7 a été amélioré, et il est beaucoup plus intelligent dans ce cas de support ANSI SQL. –

9

J'aurais dû google pendant un peu plus longtemps ... Il semble que j'ai trouvé my answer.

MySQL étend l'utilisation de GROUP BY si que vous pouvez utiliser des colonnes ou des calculs non agrégées dans la liste SELECT qui ne figurent pas dans le GROUP BY clause. Vous pouvez utiliser cette fonctionnalité pour obtenir de meilleures performances en évitant le tri inutile des colonnes et le regroupement . Par exemple, vous n'avez pas besoin de groupe sur customer.name dans la requête suivante

Dans SQL standard, vous devez ajouter customer.name à la clause GROUP BY. En MySQL, le nom est redondant.

Encore, cela semble juste ... faux.

+3

Vous avez raison de dire que cela semble faux. C'est! Bien que je sois certain qu'il y a des exceptions, comme l'a souligné Bill Karwin, trop souvent j'ai vu des développeurs, qui ne connaissent pas assez bien les données ou comment cette fonctionnalité fonctionne vraiment, écrire des requêtes avec des clauses group by et obtenir de mauvais résultats. Cette fonctionnalité doit être désactivée par défaut et autorisée à être volontairement remplacée par une option de requête à utiliser dans les cas où l'ingénieur est suffisamment informé pour l'utiliser. –

+0

Il n'y a pas plus de "faux" que d'avoir un résultat "SELECT * FROM table1'" dans un ordre donné et cohérent: c'est une fonctionnalité, pas un bug. – kmoser

-1
select * from personel where p_id IN(select 
min(dbo.personel.p_id) 
FROM 
personel 
GROUP BY dbo.personel.p_adi) 
+1

Cela ne répond certainement pas à la question – Ojen

+0

@Ojen Ce n'est pas le cas, mais il explique _kind-of_ ce qui se passe. Le code ci-dessus est un exemple de la façon dont ce comportement non standard peut être modélisé en utilisant SQL standard. – Griddo

Questions connexes