Database
 sql >> Base de données >  >> RDS >> Database

Comment sélectionner la première ligne de chaque groupe GROUP BY

Problème :

Vous avez regroupé vos données avec GROUP BY et souhaite afficher uniquement la première ligne de chaque groupe.

Exemple :

Notre base de données a une table nommée exam_results avec les données dans le tableau suivant :

prénom nom_de_famille année résultat
Jean Klein 2020 40
Édith Noir 2020 43
Marquer Johnson 2019 32
Laura Été 2020 35
Kate Smith 2019 41
Jacob Noir 2019 44
Tom Bennett 2020 38
Émilie Kelly 2020 43

Pour chaque année, trouvons l'étudiant avec le meilleur result . S'il y a deux étudiants à égalité pour le meilleur dans un groupe, nous sélectionnerons arbitrairement l'un d'entre eux à afficher.

Solution :

WITH added_row_number AS (
  SELECT
    *,
    ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number
  FROM exam_results
)
SELECT
  *
FROM added_row_number
WHERE row_number = 1;

Le résultat est :

prénom nom_de_famille année résultat numéro_ligne
Jacob Noir 2019 44 1
Émilie Kelly 2020 43 1

Discussion :

Tout d'abord, vous devez écrire un CTE dans lequel vous attribuez un numéro à chaque ligne de chaque groupe. Pour ce faire, vous pouvez utiliser le ROW_NUMBER() une fonction. Dans OVER() , vous spécifiez les groupes dans lesquels les lignes doivent être divisées (PARTITION BY ) et l'ordre dans lequel les numéros doivent être attribués aux lignes (ORDER BY ).

Jetez un œil au résultat de la requête interne :

SELECT
  *,
  ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number
FROM exam_results;
prénom nom_de_famille année résultat numéro_ligne
Jacob Noir 2019 44 1
Kate Smith 2019 41 2
Marquer Johnson 2019 32 3
Émilie Kelly 2020 43 1
Édith Noir 2020 43 2
Jean Klein 2020 40 3
Tom Bennett 2020 38 4
Laura Été 2020 35 5

Vous attribuez les numéros de ligne dans chaque groupe (c'est-à-dire l'année). Chaque ligne a un numéro de ligne basé sur la valeur du result colonne. Les lignes sont triées dans l'ordre décroissant à cause du DESC mot-clé après ORDER BY result . Même s'il y a plusieurs lignes dans un groupe qui ont la même valeur de result , les lignes reçoivent toujours des numéros différents. Ici, Edith Black et Emily Kelly ont le même result mais des numéros de lignes différents. Pour modifier ce comportement et attribuer le même numéro de ligne pour le même résultat au sein d'un groupe, utilisez RANK() ou DENSE_RANK() au lieu de ROW_NUMBER() .

Dans la requête externe, vous sélectionnez toutes les données du CTE (added_row_number ) et utilisez un WHERE condition pour spécifier quelle ligne afficher à partir de chaque groupe. Ici, nous voulons afficher la première ligne, donc la condition est row_number = 1 .

Notez que vous pouvez facilement modifier la solution pour obtenir, par exemple, la deuxième ligne de chaque groupe.

WITH added_row_number AS (
  SELECT
    *,
    ROW_NUMBER() OVER(PARTITION BY year ORDER BY result DESC) AS row_number
  FROM exam_results
)
SELECT
  *
FROM added_row_number
WHERE row_number = 2;

Voici le résultat :

prénom nom_de_famille année résultat numéro_ligne
Kate Smith 2019 41 2
Édith Noir 2020 43 2

D'autre part, si vous souhaitez obtenir la ou les lignes avec la deuxième valeur la plus élevée de result au sein de chaque groupe, vous devez utiliser le DENSE_RANK() une fonction. Alors que le ROW_NUMBER() la fonction crée des nombres consécutifs pour chaque ligne d'un groupe, ce qui donne différentes valeurs attribuées aux lignes avec le même résultat, le DENSE_RANK() la fonction donne le même numéro aux lignes avec le même résultat.

WITH added_dense_rank AS (
  SELECT
    *,
    DENSE_RANK() OVER(PARTITION BY year ORDER BY result DESC) AS rank
  FROM exam_results
)
SELECT
  *
FROM added_dense_rank
WHERE rank = 2;
prénom nom_de_famille année résultat classement
Kate Smith 2019 41 2
Jean Klein 2020 40 2

Vous pouvez voir que John Klein a la deuxième valeur la plus élevée de result (40) pour l'année 2020. John Klein est en fait la troisième personne du groupe, mais les deux premiers élèves ont le même result et ils ont tous les deux rank = 1 .