SQL Pratique
LAG, LEAD et NTILE en SQL : guide pratique complet
12 min de lecture

LAG, LEAD et NTILE en SQL : guide pratique complet

Maîtrisez LAG, LEAD et NTILE en SQL avec des exemples concrets et des exercices pour réussir vos entretiens data. Comparatifs et FAQ inclus.

Avatar de Thomas LeroyThomas Leroy

Les fonctions LAG, LEAD et NTILE font partie des fonctions fenêtre les plus demandées en entretien SQL. Elles permettent d'accéder à des lignes voisines ou de découper un ensemble de résultats en segments égaux — sans recourir à une sous-requête coûteuse. Si vous savez déjà utiliser ROW_NUMBER ou RANK, ces 3 fonctions sont l'étape suivante pour passer au niveau supérieur. Dans ce guide, vous apprendrez leur syntaxe exacte, leurs cas d'usage concrets et les pièges à éviter en situation d'entretien.

📌 Ce qu'il faut retenir

  • LAG accède à la valeur d'une ligne précédente dans la partition courante.
  • LEAD accède à la valeur d'une ligne suivante dans la partition courante.
  • NTILE(n) divise les lignes en n groupes de taille égale (ou quasi-égale).
  • Ces 3 fonctions sont disponibles dans PostgreSQL, MySQL 8+, SQL Server et BigQuery.

Pourquoi LAG, LEAD et NTILE sont incontournables en entretien

Les recruteurs en data testent régulièrement la capacité à calculer des variations entre périodes (croissance mois sur mois, taux de churn, évolution des ventes). Sans LAG ou LEAD, ce type de calcul impose une auto-jointure ou une sous-requête corrélée — deux approches nettement plus lentes et plus difficiles à lire.

De même, segmenter des utilisateurs en quartiles (Top 25 %, Bottom 25 %) est un classique des tests techniques en analytics. NTILE résout ce problème en une seule ligne là où un CASE WHEN à la main nécessite plusieurs étapes.

Selon le classement des compétences SQL publié par Mode Analytics (2024), les fonctions fenêtre avancées figurent dans 68 % des tests techniques envoyés aux candidats data analyst. Maîtriser LAG, LEAD et NTILE vous place donc immédiatement dans le top des profils évalués.

Si vous n'avez pas encore lu notre introduction sur les fonctions fenêtre SQL, commencez par là avant de poursuivre ici.


La syntaxe commune des trois fonctions

Les 3 fonctions partagent la même structure de base : elles s'exécutent dans une clause OVER() qui définit la partition (équivalent d'un GROUP BY local) et le tri appliqué à la fenêtre.

FONCTION(expression, offset, valeur_défaut)
  OVER (
    PARTITION BY colonne_partition
    ORDER BY colonne_tri
  )
  • PARTITION BY : regroupe les lignes par catégorie (utilisateur, produit, région…). Sans cette clause, toute la table forme une seule partition.
  • ORDER BY : détermine l'ordre dans lequel les lignes sont parcourues à l'intérieur d'une partition.
  • offset (LAG / LEAD uniquement) : nombre de lignes à sauter. Par défaut : 1.
  • valeur_défaut (LAG / LEAD uniquement) : valeur renvoyée quand il n'existe pas de ligne précédente / suivante. Par défaut : NULL.

💡 Bon à savoir

Toujours renseigner la valeur par défaut de LAG et LEAD dans un contexte de production. Un NULL inattendu peut fausser un calcul de variation et passer inaperçu pendant des semaines.


LAG : comparer chaque ligne à la précédente

Syntaxe détaillée

LAG(expression [, offset [, valeur_défaut]])
  OVER (PARTITION BY ... ORDER BY ...)

Exemple 1 : évolution du chiffre d'affaires mois par mois

Imaginons une table ventes avec les colonnes mois (DATE), region (VARCHAR) et ca (NUMERIC).

SELECT
  mois,
  region,
  ca,
  LAG(ca, 1, 0) OVER (
    PARTITION BY region
    ORDER BY mois
  ) AS ca_mois_precedent,
  ca - LAG(ca, 1, 0) OVER (
    PARTITION BY region
    ORDER BY mois
  ) AS variation_absolue,
  ROUND(
    (ca - LAG(ca, 1, 0) OVER (PARTITION BY region ORDER BY mois))
    / NULLIF(LAG(ca, 1, 0) OVER (PARTITION BY region ORDER BY mois), 0) * 100,
    2
  ) AS variation_pct
FROM ventes
ORDER BY region, mois;

Ce que fait cette requête :

  • Elle calcule le CA du mois précédent pour chaque région.
  • Elle calcule la variation absolue et le pourcentage d'évolution.
  • NULLIF(..., 0) évite une division par zéro quand le CA précédent est nul.

Exemple 2 : détecter des ruptures dans une série temporelle

SELECT
  date_commande,
  id_client,
  LAG(date_commande) OVER (
    PARTITION BY id_client
    ORDER BY date_commande
  ) AS commande_precedente,
  date_commande - LAG(date_commande) OVER (
    PARTITION BY id_client
    ORDER BY date_commande
  ) AS jours_depuis_derniere_commande
FROM commandes;

Ce pattern est typique des exercices sur le taux de rétention ou la fréquence d'achat. Consultez notre article dédié sur l'exercice SQL du taux de rétention utilisateurs pour un cas complet.


LEAD : anticiper la ligne suivante

Syntaxe détaillée

LEAD(expression [, offset [, valeur_défaut]])
  OVER (PARTITION BY ... ORDER BY ...)

LEAD est le miroir exact de LAG : au lieu de regarder en arrière, il regarde en avant dans la fenêtre triée.

Exemple 1 : calculer la durée de chaque session utilisateur

SELECT
  id_session,
  id_utilisateur,
  debut_session,
  LEAD(debut_session) OVER (
    PARTITION BY id_utilisateur
    ORDER BY debut_session
  ) AS debut_session_suivante,
  LEAD(debut_session) OVER (
    PARTITION BY id_utilisateur
    ORDER BY debut_session
  ) - debut_session AS duree_entre_sessions
FROM sessions;

Exemple 2 : identifier la prochaine étape dans un funnel

SELECT
  id_utilisateur,
  etape,
  timestamp_etape,
  LEAD(etape) OVER (
    PARTITION BY id_utilisateur
    ORDER BY timestamp_etape
  ) AS etape_suivante,
  LEAD(timestamp_etape) OVER (
    PARTITION BY id_utilisateur
    ORDER BY timestamp_etape
  ) - timestamp_etape AS temps_avant_etape_suivante
FROM funnel_events;

Ce cas d'usage complète parfaitement notre guide sur l'analyse d'un tunnel de conversion e-commerce.

⚠️ Attention

Quand LEAD atteint la dernière ligne d'une partition, il renvoie NULL (ou la valeur par défaut si vous en avez défini une). Ne jamais filtrer directement sur ce résultat sans vérifier ce cas limite, au risque d'exclure des lignes importantes.


NTILE : segmenter en groupes égaux

Syntaxe détaillée

NTILE(n)
  OVER (PARTITION BY ... ORDER BY ...)

NTILE(n) attribue un numéro de groupe entre 1 et n à chaque ligne, en les répartissant le plus équitablement possible. Si le nombre de lignes n'est pas divisible par n, les premiers groupes reçoivent une ligne supplémentaire.

Exemple 1 : segmenter les clients en quartiles de dépenses

SELECT
  id_client,
  total_depenses,
  NTILE(4) OVER (ORDER BY total_depenses DESC) AS quartile
FROM (
  SELECT id_client, SUM(montant) AS total_depenses
  FROM commandes
  GROUP BY id_client
) AS depenses_client;

Résultat attendu :

  • Quartile 1 = Top 25 % des clients les plus dépensiers.
  • Quartile 4 = Bottom 25 % des clients les moins dépensiers.

Exemple 2 : créer des déciles de performance par équipe commerciale

SELECT
  id_commercial,
  equipe,
  ca_annuel,
  NTILE(10) OVER (
    PARTITION BY equipe
    ORDER BY ca_annuel DESC
  ) AS decile_performance
FROM performances_commerciales;

Ce pattern est très utilisé dans les dashboards RH et les analyses de performance commerciale.


Tableau comparatif : LAG vs LEAD vs NTILE

Critère LAG LEAD NTILE
Direction de lecture Ligne précédente Ligne suivante Sans direction — groupe global
Paramètre offset Oui (défaut : 1) Oui (défaut : 1) Non — remplacé par n
Valeur par défaut possible Oui Oui Non applicable
Retourne un rang / groupe Non — retourne une valeur Non — retourne une valeur Oui — entier entre 1 et n
Cas d'usage principal Variation période sur période Durée avant événement suivant Segmentation / classement en buckets
NULL en bord de partition Oui (première ligne) Oui (dernière ligne) Jamais

Compatibilité par moteur SQL

Moteur LAG LEAD NTILE Version minimale
PostgreSQL 8.4+
MySQL 8.0+
SQL Server 2012+
Oracle 8i+
BigQuery Standard SQL
SQLite 3.25+

Combiner LAG, LEAD et NTILE dans une seule requête

En entretien avancé, il est fréquent de devoir combiner plusieurs fonctions fenêtre dans une même requête analytique. Voici un exemple complet sur une table de ventes e-commerce :

WITH ventes_mensuelles AS (
  SELECT
    id_produit,
    DATE_TRUNC('month', date_vente) AS mois,
    SUM(montant) AS ca
  FROM ventes
  GROUP BY id_produit, DATE_TRUNC('month', date_vente)
),
analyse AS (
  SELECT
    id_produit,
    mois,
    ca,
    LAG(ca) OVER (PARTITION BY id_produit ORDER BY mois)    AS ca_precedent,
    LEAD(ca) OVER (PARTITION BY id_produit ORDER BY mois)   AS ca_suivant,
    NTILE(4) OVER (ORDER BY ca DESC)                        AS quartile_global,
    ROUND(
      (ca - LAG(ca) OVER (PARTITION BY id_produit ORDER BY mois))
      / NULLIF(LAG(ca) OVER (PARTITION BY id_produit ORDER BY mois), 0) * 100,
      1
    ) AS croissance_pct
  FROM ventes_mensuelles
)
SELECT *
FROM analyse
WHERE mois >= '2025-01-01'
ORDER BY id_produit, mois;

Cette requête :

  1. Calcule le CA mensuel par produit dans une CTE.
  2. Ajoute le CA du mois précédent et suivant avec LAG et LEAD.
  3. Attribue un quartile global sur l'ensemble des lignes avec NTILE(4).
  4. Calcule le taux de croissance mensuel en évitant la division par zéro.

💡 Bon à savoir

Utiliser une CTE en amont de vos fonctions fenêtre (comme dans l'exemple ci-dessus) améliore la lisibilité et permet au moteur SQL d'optimiser l'exécution. Évitez d'imbriquer les fonctions fenêtre directement dans une sous-requête non nommée — certains moteurs l'interdisent.


Erreurs classiques à éviter en entretien

1. Oublier l'ORDER BY dans OVER()

Sans ORDER BY, LAG et LEAD renvoient un résultat indéterminé. Le moteur SQL ne sait pas dans quel ordre lire les lignes. En entretien, cet oubli est éliminatoire.

2. Confondre PARTITION BY et GROUP BY

PARTITION BY ne réduit pas le nombre de lignes — chaque ligne du résultat reste visible. GROUP BY agrège et réduit. Une confusion entre les deux montre une compréhension insuffisante des fonctions fenêtre.

3. Négliger les cas limites de NTILE

Si vous avez 10 lignes et que vous appelez NTILE(3), les groupes auront respectivement 4, 3 et 3 lignes — pas 3, 3, 4. Le surpoids est toujours attribué aux premiers groupes. Anticiper ce comportement évite des erreurs de segmentation.

4. Réutiliser la même fenêtre sans clause WINDOW

Si vous appelez LAG et LEAD avec la même définition OVER(...) plusieurs fois, utilisez une clause WINDOW nommée pour éviter la répétition et clarifier la requête :

SELECT
  mois,
  ca,
  LAG(ca)  OVER w AS ca_precedent,
  LEAD(ca) OVER w AS ca_suivant
FROM ventes
WINDOW w AS (PARTITION BY region ORDER BY mois);

Cette syntaxe est supportée par PostgreSQL et BigQuery. Elle n'est pas disponible dans SQL Server.


Exercice pratique : taux de churn mensuel avec LAG

Contexte : vous avez une table abonnes avec id_client, mois et statut (actif ou churné). Calculez le nombre de clients ayant churné chaque mois et le taux de churn par rapport au mois précédent.

WITH actifs_par_mois AS (
  SELECT
    mois,
    COUNT(*) FILTER (WHERE statut = 'actif')   AS nb_actifs,
    COUNT(*) FILTER (WHERE statut = 'churné')  AS nb_churnes
  FROM abonnes
  GROUP BY mois
)
SELECT
  mois,
  nb_actifs,
  nb_churnes,
  LAG(nb_actifs) OVER (ORDER BY mois) AS actifs_mois_precedent,
  ROUND(
    nb_churnes::NUMERIC
    / NULLIF(LAG(nb_actifs) OVER (ORDER BY mois), 0) * 100,
    2
  ) AS taux_churn_pct
FROM actifs_par_mois
ORDER BY mois;

Ce que le recruteur évalue ici :


Questions fréquentes

LAG et LEAD fonctionnent-ils avec des valeurs non numériques ?

Oui. LAG et LEAD fonctionnent avec n'importe quel type de données : dates, chaînes de caractères, booléens. Vous pouvez par exemple comparer l'étape précédente dans un funnel (LAG(etape)) ou la catégorie du produit précédemment acheté (LAG(categorie)).

Quelle est la différence entre NTILE et PERCENT_RANK ?

NTILE(n) attribue un numéro de groupe entier (1 à n) de façon équilibrée. PERCENT_RANK retourne un ratio flottant entre 0 et 1 représentant la position relative d'une ligne dans la partition. NTILE est plus adapté à la segmentation opérationnelle, PERCENT_RANK à l'analyse statistique fine.

Peut-on utiliser LAG sur plusieurs colonnes à la fois ?

Non, une seule expression par appel de LAG. Mais rien n'empêche de multiplier les appels dans le même SELECT : LAG(ca) OVER w, LAG(quantite) OVER w, etc. La clause WINDOW nommée évite alors la répétition.

Est-ce que NTILE garantit des groupes de taille strictement égale ?

Non. Si le nombre total de lignes n'est pas divisible par n, les premiers groupes ont une ligne de plus. Avec 100 lignes et NTILE(3), vous obtenez des groupes de tailles 34 / 33 / 33.

Comment gérer un offset variable dans LAG ou LEAD ?

L'offset de LAG et LEAD doit être une constante entière au moment de la compilation. Vous ne pouvez pas passer une colonne ou une expression dynamique comme offset. Pour un décalage variable, une auto-jointure ou une CTE récursive est nécessaire.

LAG et LEAD sont-ils coûteux en performance ?

Non, contrairement à une sous-requête corrélée ou une auto-jointure, LAG et LEAD ne lisent les données qu'une seule fois grâce au mécanisme de fenêtrage. Ils sont généralement plus rapides que leurs alternatives. Sur des tables de plusieurs millions de lignes, assurez-vous que la colonne ORDER BY de la fenêtre est indexée.

Dans quel ordre SQL Server évalue-t-il les fonctions fenêtre par rapport au WHERE ?

Les fonctions fenêtre sont évaluées après le WHERE et le FROM, mais avant le SELECT final. Cela signifie que vous ne pouvez pas filtrer directement sur le résultat d'une fonction fenêtre dans le WHERE — vous devez encapsuler la requête dans une CTE ou une sous-requête, puis filtrer à l'extérieur.


Conclusion

LAG, LEAD et NTILE sont 3 fonctions fenêtre que tout data analyst doit maîtriser avant un entretien technique. LAG et LEAD permettent de comparer des lignes entre elles sans jointure coûteuse, tandis que NTILE offre une segmentation en groupes équilibrés en une seule ligne de SQL. Combinées à une CTE bien structurée, elles couvrent l'essentiel des cas d'analyse temporelle et de segmentation clients.

Pour consolider vos acquis, entraînez-vous sur des jeux de données réels et chronométrez-vous : les recruteurs attendent en général une solution complète en moins de 15 minutes. Rendez-vous sur SQL Pratique pour accéder à des exercices interactifs avec correction automatique et environnement d'exécution en ligne.

Prêt à vous entraîner ?

50 exercices SQL interactifs avec éditeur en ligne, chronomètre et feedback IA.

Voir les exercices