La clause Clause GROUP BY est utilisée pour regrouper des lignes d'une table partageant les mêmes valeurs dans toutes les colonnes précisées. L'ordre dans lequel ces colonnes sont indiquées importe peu. L'effet est de combiner chaque ensemble de lignes partageant des valeurs communes en un seul groupe de ligne représentant toutes les lignes du groupe. Ceci se fait en éliminant les redondances dans la sortie et/ou pour calculer les agrégats s'appliquant à ces groupes. Par exemple :
=> SELECT * FROM test1;
x | y
---+---
a | 3
c | 2
b | 5
a | 1
(4 rows)
=> SELECT x FROM test1 GROUP BY x;
x
---
a
b
c
(3 rows)
Dans la seconde requête, nous n'aurions pas pu écrire SELECT * FROM test1 GROUP BY x parce qu'il n'existe pas une seule valeur pour la colonne y pouvant être associé avec chaque autre groupe. Les colonnes de regroupement peuvent être référencées dans la liste de sélection car elles ont une valeur constante unique par groupe.
En général, si une table est groupée, les colonnes qui ne sont pas utilisées dans le regroupement ne peuvent pas être référencées sauf dans les expressions d'agrégats. Voici un exemple d'expressions d'agrégat :
=> SELECT x, sum(y) FROM test1 GROUP BY x;
x | sum
---+-----
a | 4
b | 5
c | 2
(3 rows)
Ici, sum est la fonction d'agrégat qui calcule une seule valeur pour le groupe entier. Plus d'informations sur les fonctions d'agrégats disponibles sont proposées dans Section 9.15.
Astuce : Le regroupement sans expressions d'agrégats calcule effectivement l'ensemble les valeurs distinctes d'une colonne. Ceci peut aussi se faire en utilisant la clause DISTINCT (voir Section 7.3.3).
Voici un autre exemple : il calcule les ventes totales pour chaque produit (plutôt que le total des ventes sur tous les produits).
SELECT produit_id, p.nom, (sum(v.unite) * p.prix) AS ventes
FROM produits p LEFT JOIN ventes v USING (produit_id)
GROUP BY produit_id, p.nom, p.prix;
Dans cet exemple, les colonnes produit_id, p.nom et p.prix doivent être dans la clause GROUP BY car elles sont référencées dans la liste de sélection de la requête. (Suivant la façon dont est conçue la table produits, le nom et le prix pourraient être totalement dépendants de l'ID du produit, donc des regroupements supplémentaires pourraient théoriquement être inutiles mais ceci n'est pas encore implémenté.) La colonne s.unite n'a pas besoin d'être dans la liste GROUP BY car elle est seulement utilisée dans l'expression de l'agrégat (sum(...)) représentant les ventes d'un produit. Pour chaque produit, la requête renvoie une ligne de résumé sur les ventes de ce produit.
En SQL strict, GROUP BY peut seulement grouper les colonnes de la table source mais PostgreSQL étend ceci en autorisant GROUP BY à grouper aussi les colonnes de la liste de sélection. Grouper par expressions de valeurs au lieu de simples noms de colonnes est aussi permis.
Si une table a été groupée en utilisant la clause GROUP BY mais que seul certains groupes sont intéressants, la clause HAVING peut être utilisée, plus comme une clause WHERE, pour éliminer les groupes d'une table groupée. Voici la syntaxe :
SELECT liste_selection FROM ... [WHERE
...] GROUP BY ... HAVING
expression_booléenne
Les expressions de la clause HAVING peuvent référer à la fois aux expressions groupées et aux expressions non groupées (ce qui impliquent nécessairement une fonction d'agrégat).
Exemple :
=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING sum(y) > 3;
x | sum
---+-----
a | 4
b | 5
(2 rows)
=> SELECT x, sum(y) FROM test1 GROUP BY x HAVING x < 'c';
x | sum
---+-----
a | 4
b | 5
(2 rows)
De nouveau, un exemple plus réaliste :
SELECT produit_id, p.nom, (sum(v.unite) * (p.prix - p.cout)) AS profit
FROM produits p LEFT JOIN ventes v USING (produit_id)
WHERE v.date > CURRENT_DATE - INTERVAL '4 weeks'
GROUP BY produit_id, p.nom, p.prix, p.cout
HAVING sum(p.prix * s.unite) > 5000;
Dans l'exemple ci-dessus, la clause WHERE sélectionne les lignes par une colonne qui n'est pas groupée (l'expression est vraie seulement pour les ventes des quatre dernières semaines) alors que la clause HAVING restreint la sortie aux groupes dont le total des ventes dépasse 5000. Notez que les expressions d'agrégats n'ont pas besoin d'être identiques dans toutes les parties d'une requête.
Partager