Window Fonctions

2025-08-28 12:44:58 +02:00
parent 6f7b0edfd3
commit 3f5181dc66
4 changed files with 419 additions and 1 deletions

@@ -2,3 +2,4 @@
- [Fonctions d'agrégation](aggregation.md) - [Fonctions d'agrégation](aggregation.md)
- [Arbres](Arbres.md) - [Arbres](Arbres.md)
- [Fonctions de fenêtrage](window.md)

@@ -1 +0,0 @@
#

213
Réponses/aggregation.sql Normal file

@@ -0,0 +1,213 @@
-- Réponses
-- 1 : Quel est le nombre total de ticket enregistrés dans la base ?
SELECT COUNT(*) AS nb_ticket
FROM ticket;
-- 2 : Calculer le chiffre daffaires global.
SELECT SUM(quantite * prix_unitaire) AS montant_total
FROM Lignesticket;
-- 3 : Pour chaque client, afficher son nom et le nombre de ticket quil a effectués.
SELECT c.nom, COUNT(t.id_ticket) AS nb_ticket
FROM client c
JOIN ticket t ON c.id_client = t.id_client
GROUP BY c.nom;
-- 4 : Calculer le montant moyen dun ticket (total ventes ÷ nombre de ticket).
SELECT AVG(total_ticket) AS panier_moyen
FROM (
SELECT t.id_ticket, SUM(lt.quantite * lt.prix_unitaire) AS total_ticket
FROM ticket t
JOIN Lignesticket lt ON t.id_ticket = lt.id_ticket
GROUP BY t.id_ticket
) sous_requete;
-- 5 : Afficher le produit le plus cher et le produit le moins cher (avec leur prix).
SELECT nom_produit, prix_unitaire
FROM Produits
ORDER BY prix_unitaire DESC
LIMIT 1;
SELECT nom_produit, prix_unitaire
FROM Produits
ORDER BY prix_unitaire ASC
LIMIT 1;
-- 6 : Pour chaque famille de produits, afficher le nom de la famille et le nombre de produits associés.
SELECT f.nom_famille, COUNT(p.id_produit) AS nb_produits
FROM Familles f
LEFT JOIN Produits p ON f.id_famille = p.id_famille
GROUP BY f.nom_famille;
-- 7 : Afficher, pour chaque mois, le chiffre daffaires réalisé.
SELECT DATE_TRUNC('month', t.date_vente) AS mois,
SUM(lt.quantite * lt.prix_unitaire) AS montant_mensuel
FROM ticket t
JOIN Lignesticket lt ON t.id_ticket = lt.id_ticket
GROUP BY DATE_TRUNC('month', t.date_vente)
ORDER BY mois;
-- 8 : Trouver le client qui a dépensé le plus en montant total.
SELECT c.nom, SUM(lt.quantite * lt.prix_unitaire) AS total_depense
FROM client c
JOIN ticket t ON c.id_client = t.id_client
JOIN Lignesticket lt ON t.id_ticket = lt.id_ticket
GROUP BY c.nom
ORDER BY total_depense DESC
LIMIT 1;
-- 9 : Afficher le produit ayant généré le plus de ventes en quantité totale vendue.
SELECT p.nom_produit, SUM(lt.quantite) AS total_vendu
FROM Produits p
JOIN Lignesticket lt ON p.id_produit = lt.id_produit
GROUP BY p.nom_produit
ORDER BY total_vendu DESC
LIMIT 1;
-- 10 : Pour chaque famille, afficher le montant total des ventes.
SELECT f.nom_famille, SUM(lt.quantite * lt.prix_unitaire) AS total_famille
FROM Familles f
JOIN Produits p ON f.id_famille = p.id_famille
JOIN Lignesticket lt ON p.id_produit = lt.id_produit
GROUP BY f.nom_famille
ORDER BY total_famille DESC;
-- ----------
-- 1 : Intersection (INNER JOIN)
SELECT t.id_ticket, t.date_vente, c.nom AS client
FROM ticket t
INNER JOIN client c ON c.id_client = t.id_client;
-- 2 : LEFT JOIN (client sans ticket)
SELECT c.id_client, c.nom, COUNT(t.id_ticket) AS nb_ticket
FROM client c
LEFT JOIN ticket t ON t.id_client = c.id_client
GROUP BY c.id_client, c.nom
ORDER BY c.nom;
-- 3 : RIGHT JOIN (ticket sans client)
SELECT t.id_ticket, t.date_vente, c.nom AS client
FROM client c
RIGHT JOIN ticket t ON c.id_client = t.id_client;
-- 4 : FULL OUTER JOIN (union)
SELECT
c.id_client, c.nom,
t.id_ticket, t.date_vente,
CASE
WHEN t.id_ticket IS NULL THEN 'client_sans_ticket'
WHEN c.id_client IS NULL THEN 'ticket_sans_client'
ELSE 'apparié'
END AS statut
FROM client c
FULL OUTER JOIN ticket t ON c.id_client = t.id_client
ORDER BY statut, c.nom NULLS LAST, t.id_ticket;
-- 5 : SEMI-JOIN (existence)
--Lister uniquement les client qui ont acheté au moins un produit. en SQL standard, cela se fait avec EXISTS.
--Lister tous les produits et indiquer sils ont é vendus au moins une fois (utiliser un CASE ou COALESCE).
SELECT
p.id_produit,
p.nom_produit,
CASE WHEN EXISTS (
SELECT 1
FROM Lignesticket lt
WHERE lt.id_produit = p.id_produit
) THEN TRUE ELSE FALSE END AS deja_vendu
FROM Produits p
ORDER BY p.nom_produit;
SELECT
p.id_produit,
p.nom_produit,
COALESCE(SUM(CASE WHEN lt.id_ligne IS NOT NULL THEN 1 ELSE 0 END), 0) > 0 AS deja_vendu
FROM Produits p
LEFT JOIN Lignesticket lt ON lt.id_produit = p.id_produit
GROUP BY p.id_produit, p.nom_produit;
-- 6 : ANTI-JOIN (différence)
Lister les client qui nont jamais acheté de produit.
utiliser NOT EXISTS ou une jointure + WHERE.
-- 7 : Jointure avec condition multiple
Lister les produits avec leur famille, même si certains produits nont pas de famille renseignée.
-- 8 : Auto-join (self join)
SELECT DISTINCT
c1.nom AS client_1,
c2.nom AS client_2
FROM client c1
JOIN ticket t1 ON t1.id_client = c1.id_client
JOIN Lignesticket lt1 ON lt1.id_ticket = t1.id_ticket
JOIN Lignesticket lt2 ON lt2.id_produit = lt1.id_produit
JOIN ticket t2 ON t2.id_ticket = lt2.id_ticket
JOIN client c2 ON c2.id_client = t2.id_client
WHERE c1.id_client < c2.id_client;
-- 9 : Produit cartésien
Lister toutes les combinaisons possibles de familles et de client (sans condition de jointure).
Attention : nombre de lignes = nb_familles × nb_client.
-- 10 : Jointure filtrée
Lister les ticket contenant au moins un produit de la famille "Boissons".
SELECT DISTINCT c.id_client, c.nom
FROM client c
JOIN ticket t ON t.id_client = c.id_client
JOIN Lignesticket lt ON lt.id_ticket = t.id_ticket
JOIN Produits p ON p.id_produit = lt.id_produit
JOIN Familles f ON f.id_famille = p.id_famille
WHERE f.nom_famille = 'Boissons'
ORDER BY c.nom;
-- 11
SELECT c.id_client, c.nom
FROM client c
WHERE NOT EXISTS (
SELECT 1
FROM ticket t
JOIN Lignesticket lt ON lt.id_ticket = t.id_ticket
JOIN Produits p ON p.id_produit = lt.id_produit
JOIN Familles f ON f.id_famille = p.id_famille
WHERE t.id_client = c.id_client
AND f.nom_famille = 'Boissons'
)
ORDER BY c.nom;
SELECT c.id_client, c.nom
FROM client c
LEFT JOIN (
SELECT DISTINCT t.id_client
FROM ticket t
JOIN Lignesticket lt ON lt.id_ticket = t.id_ticket
JOIN Produits p ON p.id_produit = lt.id_produit
JOIN Familles f ON f.id_famille = p.id_famille
WHERE f.nom_famille = 'Boissons'
) acheteurs_boissons
ON acheteurs_boissons.id_client = c.id_client
WHERE acheteurs_boissons.id_client IS NULL
ORDER BY c.nom;
-- total des ventes par famille
SELECT
f.nom_famille,
SUM(lt.quantite * lt.prix_unitaire) AS total_ventes
FROM Familles f
JOIN Produits p ON p.id_famille = f.id_famille
JOIN Lignesticket lt ON lt.id_produit = p.id_produit
GROUP BY f.nom_famille
ORDER BY total_ventes DESC;

205
window.md Normal file

@@ -0,0 +1,205 @@
# Fonctions de fenêtrage
> Les fonctions de fenêtrage permettent de réaliser des calculs sur un ensemble de lignes (fenêtre) sans les regrouper comme le ferait une agrégation classique avec GROUP BY.
Elles sont très utiles pour calculer des sommes cumulées, des rangs, des moyennes mobiles, ou comparer une ligne avec dautres.
Contrairement aux agrégations, elles néliminent pas le détail des lignes : chaque ligne reste présente, mais enrichie avec des colonnes calculées sur un ensemble de ligne environnantes.
### Implémentation
- Oracle - version 8 en 2000,
- SQL Server - version 2005,
- PostgreSQL - version 8.4 en 2009,
- MariaDB - version 10.2 en 2016,
- MySQL - version 8 en 2018.
### Syntaxe générale
Les fonctions de fenêtrage s'écrivent en ajoutant la clause OVER() après la fonction.
Toute fonction sans clause _OVER_ n'est pas une fonction de fenêtrage, mais plutôt une fonction d'agrégation ou à ligne unique (scalaire).
```sql
fonction_de_fenêtrage() OVER (
[PARTITION BY colonne1, colonne2, ...]
[ORDER BY colonne3, ...]
[frame_clause]
)
```
**OVER()** : définit la fenêtre dans laquelle la fonction sera appliquée.
**PARTITION BY** : divise les lignes en groupes, un peu comme un GROUP BY, mais sans agrégation.
**ORDER BY** : définit l'ordre dans lequel les lignes sont traitées à l'intérieur de chaque partition.
## Fonctions
### Fonctions d'agrégation en mode fenêtre
Ces fonctions sont identiques aux fonctions d'agrégation classiques mais appliquées ici sur les fenêtres.
SUM() → somme
AVG() → moyenne
MIN() → minimum
MAX() → maximum
COUNT() → décompte
### Fonctions de classement
#### row_number()
La fonction fenêtre `ROW_NUMBER()` en SQL attribue un numéro de ligne unique et consécutif à chaque ligne d'un ensemble de résultats, selon l'ordre spécifié.
ROW_NUMBER() attribue un numéro unique à chaque ligne, même si les valeurs sont identiques dans la colonne de tri.
#### rank (rang)
En SQL, la fonction fenêtre `RANK()` attribue un rang à chaque ligne dans un ensemble de résultats, basé sur l'ordre spécifié. Le rang commence à 1 pour la première ligne. Contrairement à la fonction ROW_NUMBER(), qui attribue des numéros de ligne consécutifs, RANK() attribue le même rang à des lignes ayant des valeurs égales dans la colonne utilisée pour le tri, mais laisse des "trous" dans la numérotation.
- 1
- 2
- 2
- 4
- 5
#### dense_rank (rang dense)
La fonction fenêtre `DENSE_RANK()` en SQL attribue un rang à chaque ligne dans un ensemble de résultats, tout en attribuant le même rang aux lignes ayant des valeurs identiques, sans sauter de rangs dans la numérotation (contrairement à RANK() qui laisse des "trous").
- 1
- 2
- 2
- 3
- 4
#### ntile
La fonction fenêtre `NTILE()` en SQL divise un ensemble de résultats en un nombre spécifié de groupes égaux (ou presque égaux) et attribue un numéro de groupe à chaque ligne. Cela permet de classer les lignes dans des partitions, souvent appelées "quartiles", "quintiles" ou autres, selon le nombre de groupes que vous spécifiez.
```sql
NTILE(4) OVER (ORDER BY colonne1) AS quartile
```
### Fonctions de distribution
#### percent_rank (rang relatif)
La fonction fenêtre `PERCENT_RANK()` en SQL calcule le rang relatif d'une ligne par rapport à l'ensemble des résultats, sous forme de pourcentage. Elle attribue une valeur comprise entre 0 et 1, qui indique où se situe la ligne dans l'ensemble trié. Le premier élément a toujours une valeur de 0 et le dernier une valeur proche de 1 (mais jamais exactement 1, sauf dans certains cas spécifiques).
#### cume_dist (distribution cumulative)
La fonction de fenêtrage `CUME_DIST()` est une fonction analytique SQL qui calcule la distribution cumulative d'une ligne dans un ensemble de résultats. Elle renvoie la fraction ou le pourcentage des lignes d'un ensemble de données qui ont une valeur inférieure ou égale à celle de la ligne actuelle, en fonction d'un certain ordre.
#### PERCENTILE_CONT (percentile continu)
#### PERCENTILE_DISC (percentile discret)
### Fonctions de navigation
#### lead (valeur suivante)
La fonction de fenêtrage `LEAD()` en SQL est utilisée pour accéder à la valeur dune ligne suivante par rapport à la ligne actuelle dans un ensemble de résultats, sans avoir à écrire une sous-requête complexe. Elle permet de "regarder" en avant dans les données d'une fenêtre pour obtenir une valeur future, ce qui peut être utile pour comparer des enregistrements successifs.
```sql
LEAD(column_1) OVER (ORDER BY column_2)
```
column_1 est le nom de la colonne de la ligne suivante que vous souhaitez inclure dans la ligne courante.
ORDER BY est obligatoire lorsque LEAD() est utilisé. La séquence des lignes doit être prévisible, sinon la fonction n'a pas de sens. Toutefois, l'ordre choisi n'est pas nécessairement le même que celui du rapport final.
column_2 est la colonne qui défini l'ordre des lignes lors de la récupération de la valeur suivante. Vous pouvez spécifier plus d'une colonne.
#### lag (valeur précédente)
La fonction de fenêtrage `LAG()` en SQL permet d'accéder à la valeur dune ligne précédente par rapport à la ligne actuelle dans un ensemble de résultats. Cela est utile pour comparer des enregistrements successifs ou calculer des différences entre la valeur actuelle et une valeur passée. C'est l'inverse de la fonction LEAD(), qui accède aux lignes suivantes.
```sql
LAG(column_1) OVER (ORDER BY column_2)
```
column_1 est le nom de la colonne de la ligne précédente que vous souhaitez inclure dans la ligne courante.
ORDER BY est obligatoire lorsque LEAD() est utilisé. La séquence des lignes doit être prévisible, sinon la fonction n'a pas de sens. Toutefois, l'ordre choisi n'est pas nécessairement le même que celui du rapport final.
column_2 est la colonne qui défini l'ordre des lignes lors de la récupération de la valeur suivante. Vous pouvez spécifier plus d'une colonne.
#### first_value (première valeur)
first_value()
```sql
```
#### last_value (dernière valeur)
last_value()
```sql
```
#### nth_value (nième valeur)
nth_value()
```sql
```
## partition
La clause PARTITION BY est une pièce maîtresse des fonctions de fenêtrage, car elle permet de découper logiquement le jeu de résultats avant dappliquer les calculs.
Elle fonctionne comme un GROUP BY, mais sans regrouper les lignes.
Chaque partition est un "sous-ensemble" indépendant sur lequel la fonction de fenêtre sapplique.
Les partitions sont recalculées pour chaque ligne, et le résultat est répété pour toutes les lignes de la partition.
## frame clause
La clause de fenêtrage définit quelles lignes autour de la ligne courante font partie de la fenêtre.c
Sans frame, SQL choisit une fenêtre par défaut :
- Avec un ORDER BY → la fenêtre va de toutes les lignes depuis le début jusquà la ligne courante.
- Sans ORDER BY → toute la partition.
### Syntaxe
```sql
{ROWS | RANGE | GROUPS}
BETWEEN borne_inf AND borne_sup
```
- ROWS : défini par un nombre de lignes physiques.
- RANGE : défini par des valeurs (basé sur lORDER BY).
- GROUPS : défini par des groupes dégalité dans lORDER BY
### Bornes
#### unbouded preceding
depuis le début de la partition
#### preceding
n lignes/valeurs avant la ligne courante
#### current row
la ligne courante
#### following
n lignes/valeurs après la ligne courante
#### unbouded following
jusquà la fin de la partition