web-dev-qa-db-fra.com

Sélectionnez plusieurs colonnes (fonction non agrégée) avec GROUP BY

J'essaie de sélectionner la valeur maximale d'une colonne, tout en regroupant par une autre colonne d'ID non unique qui a plusieurs valeurs en double. La base de données d'origine ressemble à ceci:

mukey    | comppct_r | name | type
65789    | 20        | a    | 7n
65789    | 15        | b    | 8m
65789    | 1         | c    | 1o
65790    | 10        | a    | 7n
65790    | 26        | b    | 8m
65790    | 5         | c    | 1o
...

Cela fonctionne très bien en utilisant:

SELECT c.mukey, Max(c.comppct_r) AS ComponentPercent
FROM c
GROUP BY c.mukey;

Qui renvoie une table comme:

mukey    | ComponentPercent
65789    | 20
65790    | 26
65791    | 50
65792    | 90

Je veux pouvoir ajouter d'autres colonnes sans affecter la fonction GROUP BY, pour inclure des colonnes comme le nom et le type dans la table de sortie comme:

mukey    | comppct_r | name | type
65789    | 20        | a    | 7n
65790    | 26        | b    | 8m
65791    | 50        | c    | 7n
65792    | 90        | d    | 7n

mais il génère toujours une erreur indiquant que je dois utiliser une fonction d'agrégation avec l'instruction select. Comment dois-je procéder?

15
Dylansq

Vous avez vous-même un problème le plus grand-n-par-groupe . C'est l'une des solutions possibles:

select c.mukey, c.comppct_r, c.name, c.type
from c yt
inner join(
    select c.mukey, max(c.comppct_r) comppct_r
    from c
    group by c.mukey
) ss on c.mukey = ss.mukey and c.comppct_r= ss.comppct_r

Autre approche possible, même sortie:

select c1.*
from c c1
left outer join c c2
on (c1.mukey = c2.mukey and c1.comppct_r < c2.comppct_r)
where c2.mukey is null;

Il y a une réponse complète et explicative sur le sujet ici: SQL Sélectionnez uniquement les lignes avec la valeur maximale sur une colonne

21
Adrian Carneiro

Toute colonne non agrégée doit être présente dans la clause Group By .. pourquoi ??

                  t1
x1           y1           z1
1            2             5
2            2             7

Maintenant, vous essayez d'écrire une requête comme:

select x1,y1,max(z1) from t1 group by y1;

Maintenant, cette requête ne produira qu'une seule ligne, mais quelle devrait être la valeur de x1 ?? Il s'agit essentiellement d'un comportement non défini. Pour résoudre ce problème, SQL supprimera cette requête par erreur.

Maintenant, pour en venir au point, vous pouvez soit choisir la fonction d'agrégation pour x1, soit ajouter x1 au groupe par. Notez que tout cela dépend de vos besoins.

Si vous souhaitez que toutes les lignes avec agrégation sur z1 soient regroupées par y1, vous pouvez utiliser l'approche SubQ.

Select x1,y1,(select max(z1) from t1  where tt.y1=y1 group by y1)
 from t1 tt;

Cela produira un résultat comme:

                  t1
x1           y1           max(z1)
1            2             7
2            2             7
1
user2407394

Essayez d'utiliser une table virtuelle comme suit:

SELECT vt.*,c.name FROM(
SELECT c.mukey, Max(c.comppct_r) AS ComponentPercent
FROM c
GROUP BY c.muke;
) as VT, c 
WHERE VT.mukey = c.mukey 
0
rgrano

Vous ne pouvez pas simplement ajouter des colonnes supplémentaires sans les ajouter au GROUP BY ou en appliquant une fonction d'agrégation. La raison en est que les valeurs d'une colonne peuvent être différentes à l'intérieur d'un groupe. Par exemple, vous pouvez avoir deux lignes:

mukey    | comppct_r | name | type
65789    | 20        | a    | 7n
65789    | 20        | b    | 9f

À quoi devrait ressembler le groupe agrégé pour les colonnes name et type?

Si le nom et le type sont toujours les mêmes dans un groupe, ajoutez-les simplement au GROUP BY clause:

SELECT c.mukey, Max(c.comppct_r) AS ComponentPercent
FROM c
GROUP BY c.muke, c.name, c.type;
0
nif