Extraction de données groupées
Les requêtes examinées jusqu’à maintenant produisent des lignes qui sont généralement une correspondance une pour une avec les lignes d’une table de la clause FROM
Nous allons maintenant examiner comment il est possible d’extraire d’une table, ou d’une jointure, des informations sur les concepts latents dans ces tables.
Le concept
Considérons la table Clients
.
On peut y voir, virtuellement du moins, des groupes de clients qui ont la même ville
ou la même catégorie
.
On pourrait dire que certains clients habitent la même ville, ou appartiennent à la même catégorie.
1SELECT numero, nom, prenom, ville, categorie2FROM Clients;
Clients | ||||
---|---|---|---|---|
numero | nom | prenom | ville | categorie |
1 | Lebreton | Éléonore | Meylan | A1 |
2 | Vasseur | Georges | Grenoble | B2 |
3 | Da Costa | Léon | Paris | C3 |
4 | Rey | Michel | Vizille | B1 |
5 | Weiss | Dominique | Lille | B2 |
6 | Berlioz | Diane | Paris | |
7 | Lefevre | Jean | Lyon | C1 |
9 | Barda | Jérôme | Paris | C1 |
8 | Jacques | Chaoui | Paris | C1 |
GROUP BY
Par exemple, on souhaite obtenir le nombre de clients par ville, ainsi que la valeur moyenne de leur compte.
1SELECT2 ville,3 COUNT(*) AS 'Nombre de clients',4 AVG(compte) AS 'Moyenne des comptes'5FROM Clients6GROUP BY ville ;
Moyennes des clients par ville | ||
---|---|---|
ville | Nombre de clients | Moyenne des comptes |
Grenoble | 1 | 1903 |
Lille | 1 | 0 |
Lyon | 1 | -1000 |
Meylan | 1 | 10000 |
Paris | 4 | 29461 |
Vizille | 1 | 3251 |
HAVING
Des conditions de sélection peuvent être imposées aux groupes sélectionnés.
Elles sont exprimées dans une clause HAVING
pour éviter toute confusion avec la clause WHERE
Par exemple, si on reprend la requête précédente, on peut ajouter une condition pour ne retenir que les villes avec au moins 3 clients.
1SELECT2 ville,3 COUNT(*) AS 'Nombre de clients',4 AVG(compte) AS 'Moyenne des comptes'5FROM Clients6GROUP BY ville7HAVING COUNT(*) >= 3 ;
Moyennes des clients par ville qui ont au moins 3 clients | ||
---|---|---|
ville | Nombre de clients | Moyenne des comptes |
Paris | 4 | 29461 |
Exemples
Exemple 1
Donner la liste des clients ayant commandé au moins 2 fois le produit numéro 1
(le produit numéro 1 est le T-shirt
)
Réalisons cette requête par étapes :
- On sélectionne les lignes de la table
Commandes
en les regroupant par client :
1SELECT numeroClient , COUNT(*)2FROM Commandes3GROUP BY numeroClient;
Commandes regroupées par client | |
---|---|
numeroClient | COUNT(*) |
1 | 2 |
3 | 1 |
4 | 2 |
5 | 2 |
Exemple 1
- On ne retient que les groupes d’un moins 2 commandes :
1SELECT numeroClient , COUNT(*)2FROM Commandes3GROUP BY numeroClient4HAVING COUNT(*) >= 2;
Commandes regroupées par client ayant au moins 2 commandes | |
---|---|
numeroClient | COUNT(*) |
1 | 2 |
4 | 2 |
5 | 2 |
Exemple 1
- Finalement, on ne considère, avant groupement, que les commandes spécifiant le produit
numéro 1
:
1SELECT numeroClient , COUNT(*)2FROM Commandes3WHERE numero IN (SELECT numeroCommande FROM Details WHERE numeroProduit = 1 )4GROUP BY numeroClient5HAVING COUNT(*) >= 2;
Commandes regroupées par client ayant au moins 2 commandes du produit numéro 1 | |
---|---|
numeroClient | COUNT(*) |
4 | 2 |
5 | 2 |
Exemple 1
- Pour aller plus loin, on pourrait effectuer une jointure supplémentaire pour sélectionner le produit non pas par son numéro, mais par son nom :
1SELECT numeroClient, COUNT(*)2FROM Commandes3WHERE numero IN (SELECT numeroCommande FROM Details, Produits P WHERE Details.numeroProduit = P.numero AND P.nom = 'T-shirt' )4GROUP BY numeroClient5HAVING COUNT(*) >= 2;
Commandes regroupées par client ayant au moins 2 commandes du produit T-Shirt | |
---|---|
numeroClient | COUNT(*) |
4 | 2 |
5 | 2 |
Exemple 1
Syntaxes alternatives
On pourrait aussi formuler cette requête en utilisant des jointures explicites :
1SELECT P.nom, numeroClient , COUNT(*)2FROM Commandes C, Details D, Produits P3WHERE C.numero = D.numeroCommande4AND D.numeroProduit = P.numero5AND P.nom = 'T-shirt'6GROUP BY numeroClient7HAVING COUNT(*) >= 2;
Commandes regroupées par client ayant au moins 2 commandes du produit T-Shirt | ||
---|---|---|
nom | numeroClient | COUNT(*) |
T-shirt | 4 | 2 |
T-shirt | 5 | 2 |
Exemple 1
Et pour terminer, une version avec la syntaxe JOIN
:
1SELECT numeroClient , COUNT(*)2FROM Commandes C3JOIN Details D ON C.numero = D.numeroCommande4JOIN Produits P ON D.numeroProduit = P.numero5WHERE P.nom = 'T-shirt'6GROUP BY numeroClient7HAVING COUNT(*) >= 2;
Commandes regroupées par client ayant au moins 2 commandes du produit T-Shirt | |
---|---|
numeroClient | COUNT(*) |
4 | 2 |
5 | 2 |
Exemple 2
Exemple 2
On souhaite obtenir la quantité totale commandée du produit numéro 1
, toutes commandes confondues :
1SELECT numeroProduit, SUM(quantite)2FROM Details3WHERE numeroProduit = 14GROUP BY numeroProduit;
Quantité totale commandée du produit numéro 1 | |
---|---|
numeroProduit | SUM(quantite) |
1 | 27 |
Exemple 3
Pour chaque client de Lille
, donner le montant total de ses commandes
1SELECT numeroClient, SUM(quantite * prix)2FROM Commandes C, Details D, Produits P, Clients CL3WHERE C.numero = D.numeroCommande4AND D.numeroProduit = P.numero5AND C.numeroClient = CL.numero6AND CL.ville = 'Lille'7GROUP BY numeroClient;
Commandes de Lille | |
---|---|
numeroClient | SUM(quantite * prix) |
5 | 480 |
Exemple 4
Donner la quantité qui reste en stock, si on déduit les quantités commandées
1SELECT2numero, nom,3stock - ( SELECT SUM(quantite)4 FROM Details, Produits5 WHERE numeroProduit = Produits.numero) AS 'Quantité restante'6FROM Produits7GROUP BY numero;
Quantité restante en stock | ||
---|---|---|
numero | nom | Quantité restante |
1 | T-shirt | -9 |
2 | Pantalon | 11 |
3 | Chaussures | 61 |
4 | Casquette | -29 |
5 | Sac à dos | -34 |
Multi-colonnes
- Le critère de groupement peut inclure plusieurs noms de colonne
- L’ordre des colonnes est indifférent
Donner pour chaque ville le montant total commandé
1SELECT ville, SUM(quantite * prix) AS 'montant'2FROM Clients CL, Commandes C, Details D, Produits P3WHERE CL.numero = C.numeroClient4AND C.numero = D.numeroCommande5AND D.numeroProduit = P.numero6GROUP BY ville;
Montant commandé par ville | |
---|---|
ville | montant |
Lille | 480 |
Meylan | 350 |
Paris | 495 |
Vizille | 260 |
Donner pour chaque ville et produit le montant total commandé
1SELECT ville, P.numero AS 'produit', SUM(quantite * prix) AS 'montant'2FROM Clients CL, Commandes C, Details D, Produits P3WHERE CL.numero = C.numeroClient4AND C.numero = D.numeroCommande5AND D.numeroProduit = P.numero6GROUP BY ville, P.numero;
Montant commandé par ville et produit | ||
---|---|---|
ville | produit | montant |
Lille | 1 | 300 |
Lille | 2 | 60 |
Lille | 3 | 120 |
Meylan | 1 | 75 |
Meylan | 2 | 60 |
Meylan | 3 | 120 |
Meylan | 4 | 15 |
Meylan | 5 | 80 |
Paris | 1 | 100 |
Paris | 2 | 60 |
Paris | 3 | 240 |
Paris | 4 | 15 |
Paris | 5 | 80 |
Vizille | 1 | 200 |
Vizille | 2 | 60 |
Multi-colonnes
Syntaxe JOIN
1SELECT ville, P.numero AS 'produit', SUM(quantite * prix) AS 'montant'2FROM Clients CL3JOIN Commandes C ON CL.numero = C.numeroClient4JOIN Details D ON C.numero = D.numeroCommande5JOIN Produits P ON D.numeroProduit = P.numero6GROUP BY ville, P.numero;
Calculs
Le critère de groupement peut aussi inclure une expression de calcul quelconque
Donner la liste des clients en fonction de la première lettre de leur catégorie
1SELECT substr(categorie, 1, 1) AS 'Catégorie', COUNT(*) AS 'Nombre de clients'2FROM Clients3GROUP BY substr(categorie, 1, 1);
Clients par catégorie | |
---|---|
Catégorie | Nombre de clients |
1 | |
A | 1 |
B | 3 |
C | 4 |
Regrouper les clients selon leurs valeurs de COMPTE par intervalle de 1.000
1SELECT 'de' , abs(compte/1000)*1000 AS MIN, 'à' , abs(compte/1000)*1000 + 999 AS MAX, COUNT(*) AS N2FROM Clients3GROUP BY abs(compte/1000);
valeurs de COMPTE par intervalle de 1.000 | ||||
---|---|---|---|---|
'de' | MIN | 'à' | MAX | N |
de | 0 | à | 999 | 1 |
de | 1000 | à | 1999 | 1 |
de | 1500 | à | 2499 | 1 |
de | 1750 | à | 2749 | 1 |
de | 1903 | à | 2902 | 1 |
de | 2000 | à | 2999 | 1 |
de | 3251 | à | 4250 | 1 |
de | 10000 | à | 10999 | 1 |
de | 123094 | à | 124093 | 1 |
Jointures
L’extraction de données groupées est à définir avec précaution avec des jointures
Donner pour chaque ville, la somme des comptes des clients et le nombre de commandes.
On serait tenté d’écrire :
1SELECT ville, SUM(compte), COUNT(*)2FROM Clients C, Commandes M3WHERE C.numero = M.numeroClient4GROUP BY ville;
somme des comptes des clients et le nombre de commandes | ||
---|---|---|
ville | SUM(compte) | COUNT(*) |
Lille | 0 | 2 |
Meylan | 20000 | 2 |
Paris | -1500 | 1 |
Vizille | 6502 | 2 |
Ce résultat, en apparence correct, est pourtant erroné (indépendamment du fait que les clients sans commande ne sont pas repris) :
- Le résultat de la jointure n’est pas des
clients
mais descommandes
- Rappel : le résultat d’une jointure représente des entités de la table contenant la clé étrangère
- En particulier, le compte d’un client est compté autant de fois qu’il a passé une commande
- Le calcul de la somme des comptes s’effectue donc sur des ensembles de
commandes
et non desclients
- Pour répondre correctement à la question il faudra procéder en deux étapes
Omission
Il est possible d’éviter la clause GROUP BY
lorsque le concept latent dans une table est explicitement représenté par une autre table, et que le regroupement ne sert qu’à la sélection
Donner les produits dont plus de 2 unités ont été commandées en 2024
1SELECT numero, nom, stock2FROM Produits3WHERE numero IN (SELECT numeroProduit FROM Details WHERE quantite > 2)4GROUP BY numero;
Clients par intervalle de compte | ||
---|---|---|
numero | nom | stock |
1 | T-shirt | 30 |
est équivalente à
1SELECT numero, nom, stock2FROM Produits3WHERE numero IN (SELECT numeroProduit FROM Details WHERE quantite > 2);
Clients par intervalle de compte | ||
---|---|---|
numero | nom | stock |
1 | T-shirt | 30 |
Conclusion
- Les regroupements de données permettent d’extraire des informations sur des concepts latents dans les tables.
- Les clauses
GROUP BY
etHAVING
permettent de réaliser ces regroupements. - Les jointures doivent être utilisées avec précaution dans les requêtes de regroupement.
- Il est possible d’omettre la clause
GROUP BY
lorsque le concept latent est explicitement représenté par une autre table.