J'ai une table qui ressemble à cet appelant «makerar»
cname | wmname | avg
--------+-------------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Et je veux sélectionner la moyenne maximale pour chaque cname.
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
mais j'obtiendrai une erreur,
ERROR: column "makerar.wmname" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
donc je fais ça
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname, wmname;
cependant, cela ne donnera pas les résultats escomptés et la sortie incorrecte ci-dessous est affichée.
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Les résultats réels doivent être
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | usopp | 5.0000000000000000
Comment résoudre ce problème?
Remarque: ce tableau est une VUE créée à partir d'une opération précédente.
sql
group-by
aggregate-functions
postgresql-9.1
Type au hasard
la source
la source
wmname="usopp"
attendu et pas par exemplewmname="luffy"
?Réponses:
Oui, il s'agit d'un problème d'agrégation courant. Avant SQL3 (1999) , les champs sélectionnés doivent apparaître dans la
GROUP BY
clause [*].Pour contourner ce problème, vous devez calculer l'agrégat dans une sous-requête, puis la joindre à elle-même pour obtenir les colonnes supplémentaires que vous devez afficher:
Mais vous pouvez également utiliser des fonctions de fenêtre, ce qui semble plus simple:
La seule chose avec cette méthode est qu'elle affichera tous les enregistrements (les fonctions de fenêtre ne se regroupent pas). Mais il affichera le correct (c'est-à-dire au maximum au
cname
niveau)MAX
pour le pays dans chaque ligne, donc c'est à vous de décider:La solution, sans doute moins élégante, pour montrer les seuls
(cname, wmname)
tuples correspondant à la valeur maximale, est:[*]: Chose intéressante, même si le type de spécification permet de sélectionner des champs non groupés, les principaux moteurs semblent ne pas vraiment l'aimer. Oracle et SQLServer ne permettent tout simplement pas cela. Mysql le permettait par défaut, mais depuis la version 5.7, l'administrateur doit activer cette option (
ONLY_FULL_GROUP_BY
) manuellement dans la configuration du serveur pour que cette fonctionnalité soit prise en charge ...la source
MAX
(voir la réponse de @ypercube, il y a aussi une autre solution dans ma réponse) mais pas de la façon dont vous le faites. Vérifiez la sortie attendue.avg
percname
) mais elle ne restreint pas les lignes du résultat (comme le souhaite l'OP). Voir les résultats réels devraient être le paragraphe de la question.ONLY_FULL_GROUP_BY
dans MySQL 5.7 n'active pas la façon dont le standard SQL spécifie quand les colonnes peuvent être omises degroup by
(ou fait en sorte que MySQL se comporte comme Postgres). Il revient simplement à l'ancien comportement où MySQL renvoie des résultats aléatoires (= "indéterminés") à la place.Dans Postgres, vous pouvez également utiliser la
DISTINCT ON (expression)
syntaxe spéciale :la source
BY cname
?Le problème avec la spécification de champs non groupés et non agrégés dans les
group by
sélections est que le moteur n'a aucun moyen de savoir quel champ d'enregistrement il doit retourner dans ce cas. C'est d'abord? C'est la dernière? Il n'y a généralement aucun enregistrement qui correspond naturellement au résultat agrégé (min
etmax
sont des exceptions).Cependant, il existe une solution de contournement: effectuez également l'agrégation du champ requis. En posgres, cela devrait fonctionner:
Notez que cela crée un tableau de tous les noms, classés par avg, et renvoie le premier élément (les tableaux en postgres sont basés sur 1).
la source
Utilisation de la
rank()
fonction fenêtre :Remarque
L'un ou l'autre conservera plusieurs valeurs maximales par groupe. Si vous ne voulez qu'un seul enregistrement par groupe, même s'il y a plus d'un enregistrement avec une moyenne égale à max, vous devriez vérifier la réponse de @ ypercube.
la source
Pour moi, il ne s'agit pas d'un "problème d'agrégation courant", mais simplement d'une requête SQL incorrecte. La seule bonne réponse pour "sélectionner la moyenne maximale pour chaque nom de domaine ..." est
Le résultat sera:
Ce résultat répond en général à la question "Quel est le meilleur résultat pour chaque groupe?" . Nous voyons que le meilleur résultat pour l'Espagne est 5 et pour le Canada le meilleur résultat est 2. C'est vrai, et il n'y a pas d'erreur. Si nous devons afficher wmname , nous devons aussi répondre à la question: « Quelle est la Règle ? À wmname choisir résultant ensemble » Modifions un peu les données d'entrée pour clarifier l'erreur:
Quel est le résultat attendez-vous runnig cette requête:
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
? Devrait-il êtrespain+luffy
ouspain+usopp
? Pourquoi? Il n'est pas déterminé dans la requête comment choisir "mieux" wmname si plusieurs conviennent, donc le résultat n'est pas non plus déterminé. C'est pourquoi l'interpréteur SQL renvoie une erreur - la requête n'est pas correcte.En d'autres termes, il n'y a pas de bonne réponse à la question "Qui est le meilleur du
spain
groupe?" . Luffy n'est pas meilleur qu'usopp, car usopp a le même "score".la source
SELECT cname, id, MAX(avg) FROM makerar GROUP BY cname;
qui a donné cette erreur trompeuse.Cela semble fonctionner aussi
la source
J'ai récemment rencontré ce problème, en essayant de compter en utilisant
case when
, et j'ai constaté que la modification de l'ordre des instructionswhich
etcount
résout le problème:Au lieu d'utiliser - dans ce dernier, où j'ai eu des erreurs que les pommes et les oranges devraient apparaître dans les fonctions d'agrégation
la source
which
déclaration?