ProgrammationAnalyste SQL

Comment réaliser une sélection uniquement d'enregistrements uniques à partir d'une structure de données complexe contenant plusieurs doublons dans différentes colonnes, et quelle est la spécificité du fonctionnement de DISTINCT vs GROUP BY vs ROW_NUMBER() ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

L'extraction d'enregistrements uniques en SQL est devenue une tâche cruciale avec le passage massif des organisations à la conservation de données multidimensionnelles. Parfois, il est nécessaire d'afficher des lignes non répétées par combinaison de plusieurs colonnes, parfois — uniquement par une clé unique.

Historique de la question :

Les premières versions de SQL proposaient uniquement DISTINCT pour filtrer les doublons. Ensuite, des techniques structurelles sont apparues, dont GROUP BY pour l'agrégation par ensembles de valeurs uniques et des fonctions de fenêtre comme ROW_NUMBER() pour des scénarios plus flexibles de gestion des doublons, par exemple : sélection par "dernier" ou "premier" enregistrement.

Problème :

DISTINCT fonctionne uniquement au niveau des champs dans SELECT, tandis que GROUP BY nécessite des agrégations. Les fonctions de fenêtre permettent une logique avancée, mais leur utilisation entraîne souvent des erreurs si l'ordre de sélection des lignes n'est pas soigneusement pensé. Les développeurs confondent souvent ces approches, les erreurs entraînant des résultats incorrects.

Solution :

  • Utilisez DISTINCT pour obtenir des lignes uniques en fonction des champs nécessaires.
  • GROUP BY — lorsque des agrégats sont nécessaires (par exemple, somme ou date selon des paires uniques).
  • Fonctions de fenêtres (ROW_NUMBER()) — pour des tâches de type sélection "d'une ligne à partir d'un groupe de doublons selon un critère".

Exemple de code :

Obtenir un dernier enregistrement sur les commandes pour chaque client :

WITH OrdersRank AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) as rn FROM Orders ) SELECT * FROM OrdersRank WHERE rn = 1;

Caractéristiques clés :

  • DISTINCT — renvoie des lignes uniques uniquement sur les champs indiqués dans SELECT.
  • GROUP BY — nécessite si une agrégation est requise.
  • ROW_NUMBER() — extrêmement flexible pour la sélection de lignes selon le bon prioritaire/date/version.

Questions pièges.

Peut-on utiliser DISTINCT avec des fonctions d'agrégation sans GROUP BY ?

Non, les fonctions d'agrégation nécessitent un groupement, sinon il y aura une erreur de syntaxe.

SELECT COUNT(DISTINCT CustomerID) -- correct SELECT SUM(Amount), DISTINCT CustomerID -- erreur !

Que se passe-t-il si dans GROUP BY tous les champs non agrégés de SELECT ne sont pas indiqués ?

Cela provoquera une erreur dans la plupart des SGBD : tous les champs dans SELECT, à l'exception des agrégats, doivent être répertoriés dans GROUP BY.

Peut-on "supprimer" les doublons à l'aide de fonctions de fenêtre sans sous-requête ?

Non : l'utilisation de ROW_NUMBER() à l'intérieur d'un seul SELECT ne filtre pas automatiquement les "répétitions", une requête externe est nécessaire pour sélectionner les lignes souhaitées.

Erreurs typiques et anti-modèles

  • Utilisation de DISTINCT avec un grand nombre de colonnes et de lignes — chute drastique des performances.
  • GROUP BY sans agrégat nécessaire — absurde et consommateur de ressources.
  • Fonctions de fenêtres sans filtrage ultérieur — les données sont renvoyées avec des répétitions.

Exemple de la vie réelle

Cas négatif

A choisi DISTINCT sur toutes les colonnes pour une table de 20 millions de lignes : la requête a tourné des heures, résultat — timeout ou baisse de performance de la base de données.

Avantages :

  • Facile à écrire.

Inconvénients :

  • Extrêmement inefficace sur de grandes données.

Cas positif

Utilisation de fonctions de fenêtre : a obtenu uniquement le dernier enregistrement nécessaire pour le client en millisecondes; les précédents et répétés n'ont pas été chargés.

Avantages :

  • Performance très élevée.
  • Flexibilité.

Inconvénients :

  • Nécessite une architecture de requête bien conçue et une connaissance des fonctions de fenêtre.