Tutoriel sur les fonctions de fenêtrage SQL
Le guide complet pour maîtriser toutes les window functions.
Suivez l'évolution de vos ventes par ville, calculez des cumuls, des variations et des moyennes mobiles avec les fonctions analytiques SQL. Exemples concrets et requêtes prêtes à l'emploi.
Lorsqu'on souhaite suivre l'évolution d'un indicateur, comme le montant des ventes par ville, il est souvent nécessaire de réaliser des calculs sur des partitions de données (par ville, par date, etc.). Plutôt que de multiplier les jointures ou les sous-requêtes, les fonctions analytiques (Window Functions) offrent une solution élégante et performante.
Apprendre à utiliser SUM() OVER, LAG() OVER, LEAD() OVER, AVG() OVER et les fenêtres glissantes (ROWS BETWEEN) pour analyser l'évolution de vos données métier.

graphique d'évolution des ventes cumulées par ville
Les window functions permettent d'effectuer des calculs sur un ensemble de lignes définies par PARTITION BY, tout en conservant chaque ligne du résultat. Contrairement à GROUP BY, vous ne perdez pas la granularité.
<fonction>() OVER (
[PARTITION BY colonne1, colonne2, ...]
[ORDER BY colonne]
[ROWS BETWEEN ... AND ...]
)
| ville | date | montant |
|---|---|---|
| Paris | 2024-01-01 | 1500 |
| Paris | 2024-01-02 | 2000 |
| Lyon | 2024-01-01 | 1000 |
| Lyon | 2024-01-02 | 1200 |
SELECT
ville,
date,
montant,
SUM(montant) OVER (
PARTITION BY ville
ORDER BY date
) AS cumul_ventes
FROM ventes
ORDER BY ville, date;
Résultat attendu :
| ville | date | montant | cumul_ventes |
|---|---|---|---|
| Lyon | 2024-01-01 | 1000 | 1000 |
| Lyon | 2024-01-02 | 1200 | 2200 |
| Paris | 2024-01-01 | 1500 | 1500 |
| Paris | 2024-01-02 | 2000 | 3500 |
Le PARTITION BY ville redémarre le cumul à chaque changement de ville. Le ORDER BY date cumule dans l'ordre chronologique.
LAG() accède à la valeur de la ligne précédente. LEAD() accède à la valeur de la ligne suivante. Parfaits pour calculer des variations.
SELECT
ville,
date,
montant,
LAG(montant) OVER (
PARTITION BY ville
ORDER BY date
) AS montant_precedent,
montant - LAG(montant) OVER (
PARTITION BY ville
ORDER BY date
) AS variation
FROM ventes
ORDER BY ville, date;
Résultat attendu :
| ville | date | montant | montant_precedent | variation |
|---|---|---|---|---|
| Lyon | 2024-01-01 | 1000 | NULL | NULL |
| Lyon | 2024-01-02 | 1200 | 1000 | +200 |
| Paris | 2024-01-01 | 1500 | NULL | NULL |
| Paris | 2024-01-02 | 2000 | 1500 | +500 |
COALESCE(LAG(montant, 1, 0)).SELECT
ville,
date,
montant,
LAG(montant) OVER (PARTITION BY ville ORDER BY date) AS montant_precedent,
ROUND(
(montant - LAG(montant) OVER (PARTITION BY ville ORDER BY date))
/ NULLIF(LAG(montant) OVER (PARTITION BY ville ORDER BY date), 0) * 100,
2
) AS variation_pct
FROM ventes;
SELECT
ville,
date,
montant,
AVG(montant) OVER (PARTITION BY ville) AS moyenne_ville
FROM ventes;
SELECT
ville,
date,
montant,
MIN(montant) OVER (PARTITION BY ville) AS ventes_min_ville,
MAX(montant) OVER (PARTITION BY ville) AS ventes_max_ville
FROM ventes;
Une moyenne mobile (ou moyenne glissante) lisse les fluctuations en calculant la moyenne sur une fenêtre de N lignes (ex: les 3 derniers jours).
SELECT
ville,
date,
montant,
AVG(montant) OVER (
PARTITION BY ville
ORDER BY date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moyenne_mobile_3j
FROM ventes
ORDER BY ville, date;
| Clause ROWS BETWEEN | Signification |
|---|---|
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW |
Du début de la partition jusqu'à la ligne courante (cumul) |
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING |
La ligne précédente + ligne courante + ligne suivante (fenêtre de 3) |
ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING |
Fenêtre de 5 lignes (2 avant, courante, 2 après) |
ROWS BETWEEN 7 PRECEDING AND 1 PRECEDING |
Les 7 jours précédents (excluant le jour courant) |
SELECT
ville,
date,
montant,
ROW_NUMBER() OVER (PARTITION BY ville ORDER BY date) AS rang_chrono
FROM ventes;
SELECT
ville,
date,
montant,
RANK() OVER (PARTITION BY ville ORDER BY montant DESC) AS classement_ventes
FROM ventes;
ROW_NUMBER() : numérotation unique, même sur valeurs égales (1,2,3,4...)RANK() : mêmes rangs pour valeurs égales, puis saut (1,1,3,4...)DENSE_RANK() : mêmes rangs pour valeurs égales, sans saut (1,1,2,3...)Les performances des window functions dépendent beaucoup des index. Indexez (ville, date) pour les exemples ci-dessus.
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW peut être lent sur des millions de lignes. Si possible, limitez la fenêtre (ROWS BETWEEN 1000 PRECEDING...).
Utilisez EXPLAIN (ou EXPLAIN ANALYZE) pour comprendre comment le SGBD exécute vos window functions.
Les Common Table Expressions (WITH) améliorent la lisibilité quand vous enchaînez plusieurs window functions.
| Objectif | Window Function | Alternative (moins performante) | |
|---|---|---|---|
| Cumul par partition | SUM() OVER (PARTITION BY ...) |
Auto-jointure avec GROUP BY | |
| Valeur précédente | LAG() OVER |
Auto-jointure avec décalage (très lourd) | |
| Moyenne mobile | AVG() OVER (ROWS BETWEEN ...) |
Sous-requête avec range (complexe) | |
| Classement | RANK() OVER |
Variables utilisateur (MySQL) ou sous-requête |
Sans ORDER BY, SUM() OVER (PARTITION BY ville) donne la somme totale sur toute la partition (pas un cumul progressif). Le cumul nécessite ORDER BY pour définir l'ordre d'addition.
ROWS BETWEEN compte les lignes, pas les jours. Pour une fenêtre basée sur le temps (ex: 7 jours calendaires), utilisez RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW (PostgreSQL, Oracle, SQL Server 2022+).
montant / SUM(montant) OVER () donne le ratio par rapport au total général (sans PARTITION BY). SUM() OVER (PARTITION BY ville) donne le ratio par rapport au total de la ville.
Oui, mais les NULL seront regroupés ensemble selon l'ordre défini. Il est conseillé de gérer les NULL avant (avec COALESCE) ou d'utiliser NULLS FIRST/NULLS LAST (PostgreSQL) pour contrôler leur position.
Oui : ORDER BY date, heure pour un ordre chronologique fin. Les fonctions ROW_NUMBER() et RANK() utiliseront cette hiérarchie.
montant - LAG(montant, 2) OVER (PARTITION BY ville ORDER BY date) compare avec la valeur d'il y a 2 rangs. Le second paramètre de LAG() spécifie le décalage.
Les fonctions analytiques (window functions) sont des outils puissants pour analyser l'évolution d'un paramètre, comme les ventes par ville, de manière simple et efficace.
SUM() OVER (PARTITION BY ville ORDER BY date)LAG() et LEAD() pour accéder aux lignes précédentes/suivantesAVG() OVER (ROWS BETWEEN N PRECEDING AND CURRENT ROW)ROW_NUMBER(), RANK(), DENSE_RANK()