0Pricing
Coding Interview Prep · Leçon

Conserver la dernière ligne par clé

Le schéma du dernier enregistrement par client avec partitionnement par clé et tri par date

Conserver la dernière ligne par clé est une leçon Coding Interview Prep gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Coding Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Coding Interview Prep comprend 4 leçons au total.

La question de la dernière ligne par clé

« Renvoyez la commande la plus récente pour chaque client. » « Obtenez le dernier état de chaque appareil. » Ce problème de dernière ligne par clé fait partie des tâches d’entretien SQL les plus fréquentes, car il se présente constamment dans les travaux réels d’analyse.

Il s’agit d’un cas spécialisé de recherche de la première ligne par groupe : partitionnez selon la clé, triez par horodatage décroissant et conservez la première ligne. Cette leçon approfondit ce modèle et ses variantes.

Pourquoi MAX seul ne suffit pas

Une première réponse tentante consiste à utiliser MAX(order_date) avec un regroupement par client. Cela donne la date la plus récente, mais pas le reste de la ligne correspondante, ni l’identifiant de commande, le montant ou le statut.

Si l’intervieweur souhaite obtenir la ligne complète la plus récente, MAX avec GROUP BY nécessite une jointure supplémentaire avec la table, sur la clé et la date maximale, ce qui est verbeux et peut échouer en cas d’égalité. Les fonctions de fenêtre sont plus élégantes.

-- Gives the date, not the full row
SELECT customer_id, MAX(order_date) AS last_order
FROM orders
GROUP BY customer_id;

Le modèle ROW_NUMBER

Partitionnez selon la clé, triez par horodatage décroissant, et la ligne la plus récente reçoit rn = 1. Ne conservez que ces lignes pour obtenir l’enregistrement complet le plus récent de chaque clé.

C’est la réponse de référence. Elle renvoie exactement une ligne par clé, même lorsque les horodatages sont identiques, ce que sous-entend généralement « la dernière ligne ».

WITH ranked AS (
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY customer_id
      ORDER BY order_date DESC
    ) AS rn
  FROM orders
)
SELECT customer_id, order_id, order_date, amount
FROM ranked
WHERE rn = 1;

Départager les égalités d’horodatage

Deux commandes d’un même client peuvent avoir la même valeur de order_date (le même jour ou des horodatages identiques). Sans critère de départage, la ligne qui devient rn = 1 est choisie arbitrairement et peut changer d’une exécution à l’autre.

Ajoutez une clé secondaire unique, telle que order_id DESC, afin que la ligne la plus récente soit déterministe. Les intervieweurs cherchent précisément à vérifier que vous avez remarqué ce cas particulier.

ROW_NUMBER() OVER (
  PARTITION BY customer_id
  ORDER BY order_date DESC, order_id DESC
) AS rn

Dernière ligne ou toutes les égalités

Déterminez ce que signifie « dernier » lorsque les horodatages sont identiques :

  • Vous voulez exactement une ligne par clé → utilisez ROW_NUMBER avec un critère de départage.
  • Vous voulez toutes les lignes qui partagent l’horodatage maximal → utilisez plutôt RANK() = 1, qui renvoie chaque dernière ligne à égalité.

Poser cette question de clarification montre que vous comprenez la sémantique, et pas seulement la syntaxe.

WITH ranked AS (
  SELECT *,
    RANK() OVER (
      PARTITION BY customer_id ORDER BY order_date DESC
    ) AS rnk
  FROM orders
)
SELECT * FROM ranked WHERE rnk = 1;

La variante avec sous-requête corrélée

Avant que les fonctions de fenêtre ne soient universelles, la solution de la dernière ligne par clé reposait sur une sous-requête corrélée : ne conserver une ligne que si aucune autre ligne de la même clé ne possède une date supérieure.

Cette solution fonctionne, mais exécute la requête interne pour chaque ligne. Elle est donc plus lente sur les grandes tables et peu pratique en cas d’égalité. Mentionnez-la pour montrer l’étendue de vos connaissances, mais préférez la solution avec fonction de fenêtre pour de meilleures performances.

SELECT o.*
FROM orders o
WHERE o.order_date = (
  SELECT MAX(o2.order_date)
  FROM orders o2
  WHERE o2.customer_id = o.customer_id
);

Raccourci PostgreSQL avec DISTINCT ON

PostgreSQL propose une syntaxe concise : DISTINCT ON (key) conserve la première ligne de chaque clé selon l’ORDER BY. L’ORDER BY doit commencer par les mêmes colonnes de clé, puis contenir le critère de départage ou l’horodatage.

Cette syntaxe est élégante et rapide dans PostgreSQL, mais non portable. Présentez-la comme un avantage propre à ce dialecte, tout en conservant ROW_NUMBER comme solution portable par défaut.

SELECT DISTINCT ON (customer_id)
  customer_id, order_id, order_date, amount
FROM orders
ORDER BY customer_id, order_date DESC, order_id DESC;

Dernière ligne avec une condition

Les questions réelles ajoutent des filtres : « la commande terminée la plus récente pour chaque client ». Appliquez le filtre avant le classement afin que seules les lignes admissibles soient numérotées.

Placez la condition dans le WHERE de la requête interne (il s’exécute avant la fonction de fenêtre), puis prenez rn = 1 dans la requête externe. Filtrer après le classement produirait une ligne incorrecte.

WITH ranked AS (
  SELECT *,
    ROW_NUMBER() OVER (
      PARTITION BY customer_id ORDER BY order_date DESC, order_id DESC
    ) AS rn
  FROM orders
  WHERE status = 'completed'
)
SELECT * FROM ranked WHERE rn = 1;

Exemple : statut le plus récent d’un appareil

Une table status_log enregistre device_id, status et logged_at. Pour obtenir l’état actuel de chaque appareil, partitionnez selon device_id, triez selon logged_at DESC et conservez rn = 1.

C’est le mécanisme qui permet aux tableaux de bord d’afficher l’« état actuel » de nombreuses entités à partir d’un journal d’événements en ajout seulement. La même recette permet de rechercher le dernier prix, la dernière position et la dernière version.

WITH latest AS (
  SELECT device_id, status, logged_at,
    ROW_NUMBER() OVER (
      PARTITION BY device_id ORDER BY logged_at DESC
    ) AS rn
  FROM status_log
)
SELECT device_id, status, logged_at
FROM latest
WHERE rn = 1;

Remarques sur les performances

Points à mentionner pour témoigner d’un niveau avancé :

  • Un index sur (customer_id, order_date DESC) permet au moteur de lire efficacement la ligne la plus récente pour chaque clé.
  • L’approche avec fenêtre parcourt la table une seule fois ; la sous-requête corrélée, non.
  • DISTINCT ON dans PostgreSQL peut utiliser le même index et constitue souvent l’option la plus rapide sur une seule table.
  • Pour les journaux d’événements auxquels on ajoute principalement des données, envisagez une table matérialisée des données les plus récentes, actualisée de manière incrémentielle.

Erreurs fréquentes

Attention aux points suivants :

  • Utiliser MAX(date) et renvoyer uniquement la date, plutôt que la ligne complète.
  • Oublier le critère de départage, ce qui produit des résultats non déterministes lorsque les dates sont identiques.
  • Filtrer sur la condition après le classement, ce qui peut sélectionner une ligne qui aurait dû être exclue.
  • Confondre « une seule ligne la plus récente » (ROW_NUMBER) avec « toutes les lignes les plus récentes à égalité » (RANK).

Vérification rapide

Choisissez la requête correcte pour obtenir la dernière ligne de chaque clé.

Récapitulatif : dernière ligne par clé

Le modèle : PARTITION BY clé, ORDER BY horodatage DESC (plus un critère de départage unique), conservez rn = 1.

  • MAX(date) donne la date, pas la ligne complète.
  • Ajoutez toujours un critère de départage pour garantir le déterminisme.
  • Utilisez RANK() = 1 si vous voulez toutes les lignes à égalité sur l’horodatage le plus récent.
  • Les conditions de filtrage doivent figurer dans la requête interne, avant le classement.
  • DISTINCT ON de PostgreSQL est une autre solution concise et rapide, propre à ce dialecte.

Questions Fréquemment Posées

La leçon « Conserver la dernière ligne par clé » est-elle gratuite ?

Oui — le texte complet de « Conserver la dernière ligne par clé » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Coding Interview Prep, passe à CoddyKit PRO. Le cours Coding Interview Prep comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Conserver la dernière ligne par clé » ?

Le schéma du dernier enregistrement par client avec partitionnement par clé et tri par date Tu pratiques Coding Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Coding Interview Prep ?

Aucune expérience préalable n'est requise. Coding Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Conserver la dernière ligne par clé » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Coding Interview Prep ?

Oui. Chaque leçon Coding Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. N premières lignes par groupe avec ROW_NUMBER
  2. Gérer les égalités parmi les N premiers
  3. Dédupliquer les lignes en toute sécurité
  4. Conserver la dernière ligne par clé
← Retour à Coding Interview Prep