SQL · Requêtes avancées

Tutoriel sur les fonctions de fenêtrage SQL (Window Functions)

Maîtrisez ROW_NUMBER, RANK, DENSE_RANK, NTILE, LAG, LEAD et les agrégations cumulatives. Exemples concrets avec les données Capital Bikeshare de Washington DC.

Niveau : intermédiaire à avancé | Temps de lecture : 15 min | Mis à jour : avril 2026

1. Introduction aux fonctions de fenêtrage

Définition (PostgreSQL) :

Une fonction de fenêtrage effectue un calcul sur un ensemble de lignes de la table qui sont liées d'une certaine manière à la ligne courante. Contrairement aux fonctions d'agrégation classiques, l'utilisation d'une fonction de fenêtrage ne regroupe pas les lignes en une seule ligne de sortie — les lignes conservent leur identité séparée.

+156%
d'utilisation des fonctions fenêtrage depuis 2020
DB-Engines, 2025
10+
fonctions de fenêtrage à maîtriser
Ce guide en couvre 7

L'exemple le plus pratique est un total cumulé :

Total cumulatif simple
SELECT duree_secondes,
       SUM(duree_secondes) OVER (ORDER BY heure_debut) AS total_cumulatif
FROM tutoriel.vélos_partage_q1_2012;

Cette requête crée une agrégation (total_cumulatif) sans utiliser GROUP BY. Chaque ligne conserve son identité, mais affiche la somme cumulée jusqu'à cette ligne.

Données utilisées :

Ce tutoriel utilise les données du programme Capital Bikeshare de Washington DC (premier trimestre 2012). Chaque ligne représente un trajet. Les champs heure_debut et heure_fin sont au format timestamp SQL.

Schéma conceptuel des fonctions de fenêtrage SQL

Illustration du partitionnement et de l'ordre des fenêtres

2. Syntaxe de base du fenêtrage

La première partie de l'agrégation, SUM(duree_secondes), ressemble à n'importe quelle autre agrégation. L'ajout de OVER la désigne comme fonction de fenêtrage.

Traduction :

"Prendre la somme de duree_secondes sur l'ensemble du résultat, ordonné par heure_debut."

PARTITION BY : grouper sans GROUP BY

Avec PARTITION BY
SELECT station_debut,
       duree_secondes,
       SUM(duree_secondes) OVER
         (PARTITION BY station_debut ORDER BY heure_debut)
         AS total_cumulatif
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';

Explication : La requête groupe et ordonne par station_debut. Dans chaque station, elle est ordonnée par heure_debut, et le total cumulatif somme la ligne courante et toutes les lignes précédentes. Quand station_debut change, total_cumulatif recommence.

ORDER BY dans la fenêtre

Sans ORDER BY (somme complète par partition)
SELECT station_debut,
       duree_secondes,
       SUM(duree_secondes) OVER (PARTITION BY station_debut) AS total_station_debut
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';
⚠️ Remarque importante :

Vous ne pouvez pas utiliser les fonctions de fenêtrage et les agrégations standard dans la même requête. Plus précisément, vous ne pouvez pas inclure de fonctions de fenêtrage dans une clause GROUP BY.

3. Les agrégats classiques : SUM, COUNT, AVG

Sans ORDER BY (agrégation complète par partition)

SUM, COUNT, AVG sans ordre
SELECT station_debut,
       duree_secondes,
       SUM(duree_secondes) OVER (PARTITION BY station_debut) AS total_station,
       COUNT(duree_secondes) OVER (PARTITION BY station_debut) AS nb_trajets,
       AVG(duree_secondes) OVER (PARTITION BY station_debut) AS moyenne_station
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';

Avec ORDER BY (cumulatif)

Versions cumulatives
SELECT station_debut,
       duree_secondes,
       SUM(duree_secondes) OVER
         (PARTITION BY station_debut ORDER BY heure_debut) AS total_cumulatif,
       COUNT(duree_secondes) OVER
         (PARTITION BY station_debut ORDER BY heure_debut) AS nb_cumulatif,
       AVG(duree_secondes) OVER
         (PARTITION BY station_debut ORDER BY heure_debut) AS moyenne_cumulative
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';
Visualisation :

Exécutez ces deux requêtes dans votre environnement SQL. La différence entre la version avec et sans ORDER BY est fondamentale pour comprendre les fenêtres glissantes.

4. ROW_NUMBER()

ROW_NUMBER() affiche le numéro d'une ligne donnée. Il commence à 1 et numérote selon ORDER BY.

ROW_NUMBER simple
SELECT station_debut,
       heure_debut,
       duree_secondes,
       ROW_NUMBER() OVER (ORDER BY heure_debut) AS numero_ligne
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';
ROW_NUMBER avec partition (redémarrage par station)
SELECT station_debut,
       heure_debut,
       duree_secondes,
       ROW_NUMBER() OVER (PARTITION BY station_debut ORDER BY heure_debut) AS numero_ligne
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';
Cas d'usage typique :

Supprimer les doublons en ne gardant que la première occurrence (WHERE numero_ligne = 1).

5. RANK() et DENSE_RANK()

RANK() est légèrement différent de ROW_NUMBER(). Si plusieurs lignes ont la même valeur selon ORDER BY, elles reçoivent le même rang.

RANK()
SELECT station_debut,
       duree_secondes,
       RANK() OVER (PARTITION BY station_debut ORDER BY heure_debut) AS rang
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';

Différence entre RANK et DENSE_RANK

Valeur ROW_NUMBER RANK DENSE_RANK
10 1 1 1
2
3
15 4 4 2
15 5 4 2
20 6 6 3
Résumé :
  • RANK() : saute des rangs après une égalité (1,1,1,4,4,6...)
  • DENSE_RANK() : ne saute jamais de rangs (1,1,1,2,2,3...)
  • ROW_NUMBER() : attribue des numéros uniques même sur valeurs égales

6. NTILE

NTILE(nombre_de_tranches) divise les données en tranches (quartiles, quintiles, percentiles).

NTILE en action
SELECT station_debut,
       duree_secondes,
       NTILE(4) OVER (PARTITION BY station_debut ORDER BY duree_secondes) AS quartile,
       NTILE(5) OVER (PARTITION BY station_debut ORDER BY duree_secondes) AS quintile,
       NTILE(100) OVER (PARTITION BY station_debut ORDER BY duree_secondes) AS percentile
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08'
ORDER BY station_debut, duree_secondes;
⚠️ Limite de NTILE :

Si le nombre de lignes dans une partition est inférieur au nombre de tranches, NTILE ne peut pas les répartir correctement. Par exemple, avec 2 lignes et NTILE(100), vous n'aurez que des valeurs 1 et 2. Pour des percentiles précis, assurez-vous d'avoir au moins 100 lignes.

7. LAG et LEAD

LAG extrait des lignes précédentes, LEAD extrait des lignes suivantes.

LAG et LEAD
SELECT station_debut,
       duree_secondes,
       LAG(duree_secondes, 1) OVER
         (PARTITION BY station_debut ORDER BY duree_secondes) AS lag,
       LEAD(duree_secondes, 1) OVER
         (PARTITION BY station_debut ORDER BY duree_secondes) AS lead
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08'
ORDER BY station_debut, duree_secondes;

Calcul de différences entre lignes

Différence avec la ligne précédente
SELECT station_debut,
       duree_secondes,
       duree_secondes - LAG(duree_secondes, 1) OVER
         (PARTITION BY station_debut ORDER BY duree_secondes) AS difference
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08'
ORDER BY station_debut, duree_secondes;

Nettoyer les NULL (optionnel)

Supprimer les NULL avec sous-requête
SELECT *
FROM (
    SELECT station_debut,
           duree_secondes,
           duree_secondes - LAG(duree_secondes, 1) OVER
             (PARTITION BY station_debut ORDER BY duree_secondes) AS difference
    FROM tutoriel.vélos_partage_q1_2012
    WHERE heure_debut < '2012-01-08'
) sous_requete
WHERE sous_requete.difference IS NOT NULL;
Syntaxe avancée :

LAG(colonne, décalage, valeur_par_défaut) permet de spécifier une valeur par défaut au lieu de NULL.

8. Définir un alias de fenêtre (WINDOW)

Si vous utilisez plusieurs fonctions de fenêtrage avec la même définition, créez un alias avec WINDOW.

Sans alias (répétition)
SELECT station_debut,
       duree_secondes,
       NTILE(4) OVER (PARTITION BY station_debut ORDER BY duree_secondes) AS quartile,
       NTILE(5) OVER (PARTITION BY station_debut ORDER BY duree_secondes) AS quintile,
       NTILE(100) OVER (PARTITION BY station_debut ORDER BY duree_secondes) AS percentile
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08'
ORDER BY station_debut, duree_secondes;
Avec alias (plus propre)
SELECT station_debut,
       duree_secondes,
       NTILE(4) OVER ntile_window AS quartile,
       NTILE(5) OVER ntile_window AS quintile,
       NTILE(100) OVER ntile_window AS percentile
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08'
WINDOW ntile_window AS (PARTITION BY station_debut ORDER BY duree_secondes)
ORDER BY station_debut, duree_secondes;
Note : La clause WINDOW doit toujours venir après la clause WHERE et avant ORDER BY.

9. Techniques avancées

Fenêtres avec ROWS BETWEEN (frames)

Moyenne glissante sur 3 lignes
SELECT station_debut,
       heure_debut,
       duree_secondes,
       AVG(duree_secondes) OVER (
           PARTITION BY station_debut
           ORDER BY heure_debut
           ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
       ) AS moyenne_glissante
FROM tutoriel.vélos_partage_q1_2012
WHERE heure_debut < '2012-01-08';
Types de frames :
  • ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW : défaut (cumul depuis le début)
  • ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING : fenêtre glissante de 3 lignes
  • ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING : fenêtre glissante de 5 lignes

10. Comparatif des fonctions

Fonction Description Cas d'usage typique
ROW_NUMBER() Numérotation unique Suppression doublons, pagination
RANK() Rangs avec sauts (1,1,1,4...) Classements avec ex-aequo
DENSE_RANK() Rangs sans sauts (1,1,1,2...) Classements compacts
NTILE(n) Division en n tranches Quartiles, percentiles
LAG() Valeur de la ligne précédente Différence avec période précédente
LEAD() Valeur de la ligne suivante Prévision, comparaison future

11. FAQ — Fonctions de fenêtrage SQL

Quelle est la différence entre GROUP BY et PARTITION BY ?

GROUP BY réduit le nombre de lignes (une ligne par groupe). PARTITION BY dans une fonction de fenêtrage conserve toutes les lignes et ajoute une colonne calculée par groupe. Les fonctions de fenêtrage sont plus flexibles pour les calculs cumulatifs et les comparaisons entre lignes.

Peut-on utiliser ORDER BY sans PARTITION BY ?

Oui. ORDER BY seul (sans PARTITION) traite toute la table comme une seule partition. Exemple : SUM(ventes) OVER (ORDER BY date) donne un total cumulé sur tout l'historique.

Les fonctions de fenêtrage sont-elles supportées partout ?

Oui, par tous les SGBD modernes : PostgreSQL (8.4+), MySQL (8.0+), SQL Server (2005+), Oracle, SQLite (3.25+). Les versions plus anciennes peuvent ne pas les supporter.

Comment calculer un total cumulé par mois/année ?

Utilisez PARTITION BY EXTRACT(YEAR FROM date), EXTRACT(MONTH FROM date) ou DATE_TRUNC('month', date) selon votre SGBD, puis ORDER BY date.

Quelle est la performance des fonctions de fenêtrage ?

Très bonnes sur des ensembles de données de taille raisonnable (millions de lignes). Elles évitent les sous-requêtes auto-jointures coûteuses. Cependant, sur des billions de lignes, un partitionnement intelligent est recommandé.

Peut-on utiliser plusieurs ORDER BY dans une même fenêtre ?

Oui, comme dans un ORDER BY classique : ORDER BY colonne1, colonne2. Les fonctions ROW_NUMBER() et RANK() utiliseront cette hiérarchie pour départager les égalités.

Conclusion

Les fonctions de fenêtrage sont un outil puissant pour l'analyse de données avancée. Elles permettent de calculer des totaux cumulatifs, des classements, des différences entre lignes et des percentiles, tout en conservant l'intégralité des lignes.

À retenir

  • OVER() : transforme une agrégation en fonction de fenêtrage
  • PARTITION BY : divise en groupes (comme GROUP BY sans réduire)
  • ORDER BY : définit l'ordre pour les cumuls et les rangs
  • ROW_NUMBER() : numérotation unique (idéal pour supprimer doublons)
  • RANK / DENSE_RANK : classements avec ou sans sauts
  • NTILE : diviser en tranches (quartiles, percentiles)
  • LAG / LEAD : comparer avec lignes précédentes/suivantes
  • WINDOW : alias pour simplifier les requêtes complexes
Pour aller plus loin : Consultez la documentation PostgreSQL sur les fonctions de fenêtrage pour une liste complète. Et découvrez notre guide complet des fonctions SQL pour les bases.
Faites parler vos données
Apprenez les méthodes et les outils pour extraire de la valeur stratégique : Data Science : Le guide complet des méthodes et outils.
 

Recevez la veille IA & Data qui compte vraiment

 

    Analyses claires, outils concrets et tendances IA sans bruit.     Rejoignez les lecteurs de IANA Data.