Fonctions de Fenêtrage (Window Functions) dans SQLite
Les fonctions de fenêtrage sont particulièrement utiles pour :
- Calculer des pourcentages relatifs à un total
 - Déterminer des rangs ou des classements
 - Accéder aux valeurs de lignes précédentes ou suivantes
 - Calculer des moyennes mobiles ou des sommes cumulatives
 - Analyser les tendances dans les données
 
Syntaxe de base
La syntaxe générale d’une fonction de fenêtrage est la suivante :
fonction_window() OVER (   [PARTITION BY colonnes]   [ORDER BY colonnes]   [frame_clause])Où :
fonction_window()est la fonction appliquée (comme SUM, AVG, ROW_NUMBER, etc.)PARTITION BYdivise les résultats en partitions sur lesquelles la fonction est appliquéeORDER BYdéfinit l’ordre des lignes dans chaque partitionframe_clauseprécise l’ensemble des lignes constituant la fenêtre pour chaque ligne
Préparation des données d’exemple
Pour illustrer l’utilisation des fonctions de fenêtrage, créons d’abord quelques tables d’exemple :
-- Table des ventes par régionCREATE TABLE ventes_regionales (  id INTEGER PRIMARY KEY,  region TEXT,  annee INTEGER,  trimestre INTEGER,  montant REAL);
-- Insertion de données d'exempleINSERT INTO ventes_regionales (region, annee, trimestre, montant) VALUES  ('Nord', 2022, 1, 45000),  ('Nord', 2022, 2, 52000),  ('Nord', 2022, 3, 49000),  ('Nord', 2022, 4, 58000),  ('Sud', 2022, 1, 38000),  ('Sud', 2022, 2, 40000),  ('Sud', 2022, 3, 43000),  ('Sud', 2022, 4, 47000),  ('Est', 2022, 1, 32000),  ('Est', 2022, 2, 35000),  ('Est', 2022, 3, 37000),  ('Est', 2022, 4, 41000),  ('Ouest', 2022, 1, 51000),  ('Ouest', 2022, 2, 53000),  ('Ouest', 2022, 3, 56000),  ('Ouest', 2022, 4, 59000);
-- Table des employés et leurs performancesCREATE TABLE performances_employes (  id INTEGER PRIMARY KEY,  nom TEXT,  departement TEXT,  score_performance INTEGER,  salaire REAL);
-- Insertion de données d'exempleINSERT INTO performances_employes (nom, departement, score_performance, salaire) VALUES  ('Dupont', 'Ventes', 85, 45000),  ('Martin', 'Ventes', 92, 48000),  ('Durand', 'Ventes', 78, 42000),  ('Petit', 'Ventes', 95, 52000),  ('Leroy', 'Marketing', 88, 46000),  ('Moreau', 'Marketing', 79, 43000),  ('Dubois', 'Marketing', 91, 49000),  ('Simon', 'Marketing', 84, 45000),  ('Laurent', 'IT', 97, 56000),  ('Michel', 'IT', 94, 54000),  ('Lefebvre', 'IT', 89, 51000),  ('Garcia', 'IT', 86, 50000);Fonctions de classement (Ranking Functions)
ROW_NUMBER, RANK et DENSE_RANK
Ces fonctions attribuent des numéros ou des rangs aux lignes dans une partition ordonnée :
-- Classement des employés par score de performanceSELECT  nom,  departement,  score_performance,  ROW_NUMBER() OVER (ORDER BY score_performance DESC) AS position,  RANK() OVER (ORDER BY score_performance DESC) AS rang,  DENSE_RANK() OVER (ORDER BY score_performance DESC) AS rang_denseFROM performances_employes;
-- Classement des employés par départementSELECT  nom,  departement,  score_performance,  ROW_NUMBER() OVER (PARTITION BY departement ORDER BY score_performance DESC) AS position_dept,  RANK() OVER (PARTITION BY departement ORDER BY score_performance DESC) AS rang_dept,  DENSE_RANK() OVER (PARTITION BY departement ORDER BY score_performance DESC) AS rang_dense_deptFROM performances_employes;Différences entre ces fonctions :
ROW_NUMBER(): Attribue un numéro unique à chaque ligne (1, 2, 3, 4, …)RANK(): Attribue le même rang aux ex-aequo, puis saute les rangs suivants (1, 1, 3, 4, …)DENSE_RANK(): Attribue le même rang aux ex-aequo, sans sauter de rangs (1, 1, 2, 3, …)
NTILE
La fonction NTILE divise les lignes en un nombre spécifié de groupes aussi égaux que possible :
-- Diviser les employés en 3 groupes selon leur performanceSELECT  nom,  score_performance,  NTILE(3) OVER (ORDER BY score_performance DESC) AS tiersFROM performances_employes;Cette requête classe les employés en trois tiers (1 = meilleurs performances, 3 = performances plus faibles).
Fonctions d’agrégation dans les fenêtres
Les fonctions d’agrégation classiques peuvent être utilisées comme fonctions de fenêtrage :
-- Calcul de la somme cumulée des ventes par régionSELECT  region,  trimestre,  montant,  SUM(montant) OVER (PARTITION BY region ORDER BY trimestre) AS cumul_ventesFROM ventes_regionalesORDER BY region, trimestre;
-- Calcul de la moyenne mobile des ventes sur 2 trimestresSELECT  region,  trimestre,  montant,  AVG(montant) OVER (PARTITION BY region ORDER BY trimestre                     ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS moyenne_mobile_2_trimFROM ventes_regionalesORDER BY region, trimestre;
-- Pourcentage du total par régionSELECT  region,  trimestre,  montant,  ROUND(montant * 100.0 / SUM(montant) OVER (PARTITION BY region), 2) AS pourcentage_regionFROM ventes_regionalesORDER BY region, trimestre;Fonctions de navigation (Offset Functions)
Les fonctions LAG et LEAD permettent d’accéder aux valeurs des lignes précédentes ou suivantes :
-- Comparer les ventes au trimestre précédentSELECT  region,  trimestre,  montant,  LAG(montant) OVER (PARTITION BY region ORDER BY trimestre) AS montant_trim_precedent,  montant - LAG(montant) OVER (PARTITION BY region ORDER BY trimestre) AS difference,  CASE    WHEN LAG(montant) OVER (PARTITION BY region ORDER BY trimestre) IS NULL THEN NULL    ELSE ROUND((montant - LAG(montant) OVER (PARTITION BY region ORDER BY trimestre)) * 100.0 /          LAG(montant) OVER (PARTITION BY region ORDER BY trimestre), 2)  END AS pourcentage_evolutionFROM ventes_regionalesORDER BY region, trimestre;
-- Comparer avec le trimestre suivantSELECT  region,  trimestre,  montant,  LEAD(montant) OVER (PARTITION BY region ORDER BY trimestre) AS montant_trim_suivantFROM ventes_regionalesORDER BY region, trimestre;Les fonctions FIRST_VALUE et LAST_VALUE permettent d’accéder à la première ou dernière valeur de la fenêtre :
-- Écart par rapport à la meilleure performance du départementSELECT  nom,  departement,  score_performance,  FIRST_VALUE(score_performance) OVER (PARTITION BY departement ORDER BY score_performance DESC                                       RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS meilleur_score,  score_performance - FIRST_VALUE(score_performance) OVER (PARTITION BY departement ORDER BY score_performance DESC                                                          RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS ecart_meilleurFROM performances_employesORDER BY departement, score_performance DESC;Frame Clause (définition de la fenêtre)
La clause de frame vous permet de définir précisément quelles lignes constituent la fenêtre pour chaque ligne :
-- Différentes utilisations de frame clauseSELECT  region,  trimestre,  montant,  SUM(montant) OVER (PARTITION BY region) AS total_region,  SUM(montant) OVER (PARTITION BY region ORDER BY trimestre) AS cumul_jusqua_maintenant,  SUM(montant) OVER (PARTITION BY region ORDER BY trimestre                     ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS somme_mobile_3_trim,  AVG(montant) OVER (PARTITION BY region ORDER BY trimestre                     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS moyenne_jusqua_maintenantFROM ventes_regionalesORDER BY region, trimestre;Les options de frame clause les plus courantes sont :
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: Du début jusqu’à la ligne actuelleROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING: De la ligne actuelle jusqu’à la finROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING: Toutes les lignes de la partitionROWS BETWEEN n PRECEDING AND m FOLLOWING: n lignes avant et m lignes après la ligne actuelle
Applications pratiques
Analyse de tendance des ventes
-- Analyse complète des tendances de ventes par régionSELECT  region,  trimestre,  montant,  ROUND(AVG(montant) OVER (PARTITION BY region), 2) AS moyenne_region,  ROUND(montant - AVG(montant) OVER (PARTITION BY region), 2) AS ecart_moyenne,  RANK() OVER (PARTITION BY region ORDER BY montant DESC) AS rang_dans_region,  ROUND(montant * 100.0 / SUM(montant) OVER (PARTITION BY region), 2) AS pourcentage_du_total,  ROUND(montant * 100.0 / SUM(montant) OVER (), 2) AS pourcentage_total_global,  CASE    WHEN LAG(montant) OVER (PARTITION BY region ORDER BY trimestre) IS NULL THEN NULL    ELSE ROUND((montant - LAG(montant) OVER (PARTITION BY region ORDER BY trimestre)) * 100.0 /          LAG(montant) OVER (PARTITION BY region ORDER BY trimestre), 2)  END AS croissance_vs_trim_precedentFROM ventes_regionalesORDER BY region, trimestre;Analyse des salaires
-- Analyse des salaires par rapport à la moyenne du départementSELECT  nom,  departement,  salaire,  ROUND(AVG(salaire) OVER (PARTITION BY departement), 2) AS salaire_moyen_dept,  ROUND(salaire - AVG(salaire) OVER (PARTITION BY departement), 2) AS ecart_moyenne_dept,  ROUND(salaire * 100.0 / AVG(salaire) OVER (PARTITION BY departement) - 100, 2) AS pct_vs_moyenne_dept,  ROUND(salaire * 100.0 / AVG(salaire) OVER () - 100, 2) AS pct_vs_moyenne_globale,  PERCENT_RANK() OVER (PARTITION BY departement ORDER BY salaire) AS percentile_deptFROM performances_employesORDER BY departement, salaire DESC;Identification des quartiles
-- Déterminer les quartiles de performanceSELECT  nom,  departement,  score_performance,  NTILE(4) OVER (ORDER BY score_performance) AS quartile,  CASE NTILE(4) OVER (ORDER BY score_performance)    WHEN 1 THEN 'Performance faible'    WHEN 2 THEN 'Performance moyenne-basse'    WHEN 3 THEN 'Performance moyenne-haute'    WHEN 4 THEN 'Performance élevée'  END AS categorie_performanceFROM performances_employesORDER BY score_performance;Résumé des fonctions de fenêtrage
| Catégorie | Fonction | Description | Cas d’usage | 
|---|---|---|---|
| Ranking | ROW_NUMBER() | Attribue un numéro unique à chaque ligne | Numérotation, pagination | 
RANK() | Attribue le même rang aux ex-aequo avec sauts | Classements sportifs | |
DENSE_RANK() | Attribue le même rang aux ex-aequo sans sauts | Classements scolaires | |
NTILE(n) | Divise les lignes en n groupes égaux | Quartiles, déciles | |
PERCENT_RANK() | Calcule le rang relatif entre 0 et 1 | Analyse de distribution | |
| Agrégation | SUM() OVER() | Calcule la somme sur une fenêtre | Totaux, sous-totaux | 
AVG() OVER() | Calcule la moyenne sur une fenêtre | Moyennes glissantes | |
COUNT() OVER() | Compte les lignes dans une fenêtre | Densité de distribution | |
MIN()/MAX() OVER() | Trouve le min/max dans une fenêtre | Valeurs extrêmes | |
| Navigation | LAG(col, n) | Accède à la valeur n lignes avant | Comparaison avec période précédente | 
LEAD(col, n) | Accède à la valeur n lignes après | Prévision, tendance | |
FIRST_VALUE(col) | Première valeur de la fenêtre | Référence initiale, benchmark | |
LAST_VALUE(col) | Dernière valeur de la fenêtre | Valeur finale, résultat | 
Conclusion
Les fonctions de fenêtrage constituent un outil analytique puissant dans SQLite, offrant des capacités d’analyse de données avancées directement dans vos requêtes SQL. Elles vous permettent d’effectuer des calculs sophistiqués sans avoir à recourir à des requêtes imbriquées complexes ou à un traitement externe des données.
Bien que leur syntaxe puisse sembler complexe au premier abord, la maîtrise des fonctions de fenêtrage peut considérablement améliorer vos capacités d’analyse de données et simplifier votre code SQL pour des opérations comme :
- L’analyse de tendances temporelles
 - Le calcul de statistiques glissantes
 - La création de rapports comparatifs
 - L’identification de points aberrants dans vos données
 
Ces fonctions sont particulièrement utiles dans les applications d’analyse de données, de reporting financier, et de tout système nécessitant une vision contextuelle des données.