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

Méthodes pour classer les lignes dans SQL Server :ROW_NUMBER(), RANK(), DENSE_RANK() et NTILE()

SQL Server nous fournit un certain nombre de fonctions de fenêtre qui nous aident à effectuer des calculs sur un ensemble de lignes, sans avoir à répéter les appels à la base de données. Contrairement aux fonctions d'agrégation standard, les fonctions de fenêtre ne regrouperont pas les lignes en une seule ligne de sortie, elles renverront une seule valeur agrégée pour chaque ligne, en conservant les identités distinctes de ces lignes. Le terme Fenêtre ici n'est pas lié au système d'exploitation Microsoft Windows, il décrit l'ensemble de lignes que la fonction traitera.

L'un des types de fonctions de fenêtre les plus utiles est les fonctions de fenêtre de classement qui sont utilisées pour classer des valeurs de champ spécifiques et les catégoriser en fonction du rang de chaque ligne, ce qui donne une seule valeur agrégée pour chaque ligne participée. Il existe quatre fonctions de fenêtre de classement prises en charge dans SQL Server; ROW_NUMBER(), RANK(), DENSE_RANK() et NTILE(). Toutes ces fonctions sont utilisées pour calculer à leur manière le ROWID pour la fenêtre de lignes fournie.

Quatre fonctions de fenêtre de classement utilisent la clause OVER() qui définit un ensemble de lignes spécifié par l'utilisateur dans un ensemble de résultats de requête. En définissant la clause OVER(), vous pouvez également inclure la clause PARTITION BY qui détermine l'ensemble de lignes que la fonction de fenêtre traitera, en fournissant des colonnes ou des colonnes séparées par des virgules pour définir la partition. De plus, la clause ORDER BY peut être incluse, qui définit les critères de tri dans les partitions que la fonction parcourra les lignes lors du traitement.

Dans cet article, nous verrons comment utiliser pratiquement quatre fonctions de fenêtre de classement :ROW_NUMBER(), RANK(), DENSE_RANK() et NTILE(), et la différence entre elles.

Pour servir notre démo, nous allons créer une nouvelle table simple et insérer quelques enregistrements dans la table à l'aide du script T-SQL ci-dessous :

CREATE TABLE StudentScore
(
  Student_ID INT PRIMARY KEY,
  Student_Name NVARCHAR (50),
  Student_Score INT
) 

GO

INSERT INTO StudentScore VALUES (1,'Ali', 978)
INSERT INTO StudentScore VALUES (2,'Zaid', 770)
INSERT INTO StudentScore VALUES (3,'Mohd', 1140)
INSERT INTO StudentScore VALUES (4,'Jack', 770)
INSERT INTO StudentScore VALUES (5,'John', 1240)
INSERT INTO StudentScore VALUES (6,'Mike', 1140)
INSERT INTO StudentScore VALUES (7,'Goerge', 885)

Vous pouvez vérifier que les données ont bien été insérées à l'aide de l'instruction SELECT suivante :

SELECT * FROM StudentScore ORDER BY Student_Score

Avec le résultat trié appliqué, le jeu de résultats est le suivant :

ROW_NUMBER()

La fonction de fenêtre de classement ROW_NUMBER() renvoie un numéro séquentiel unique pour chaque ligne dans la partition de la fenêtre spécifiée, en commençant à 1 pour la première ligne de chaque partition et sans répéter ou sauter des numéros dans le résultat du classement de chaque partition. S'il existe des valeurs en double dans l'ensemble de lignes, les numéros d'identification de classement seront attribués de manière arbitraire. Si la clause PARTITION BY est spécifiée, le numéro de ligne de classement sera réinitialisé pour chaque partition. Dans le tableau créé précédemment, la requête ci-dessous montre comment utiliser la fonction de fenêtre de classement ROW_NUMBER pour classer les lignes du tableau StudentScore en fonction du score de chaque étudiant :

SELECT *, ROW_NUMBER() OVER( ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore

Il ressort clairement de l'ensemble de résultats ci-dessous que la fonction de fenêtre ROW_NUMBER classe les lignes du tableau en fonction des valeurs de la colonne Student_Score pour chaque ligne, en générant un numéro unique de chaque ligne qui reflète son classement Student_Score à partir du numéro 1 sans doublons ni lacunes et traiter toutes les lignes comme une seule partition. Vous pouvez également voir que les scores en double sont attribués à différents rangs de manière aléatoire :

Si nous modifions la requête précédente en incluant la clause PARTITION BY pour avoir plus d'une partition, comme indiqué dans la requête T-SQL ci-dessous :

SELECT *, ROW_NUMBER() OVER(PARTITION BY Student_Score  ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore

Le résultat montrera que la fonction de fenêtre ROW_NUMBER classera les lignes du tableau en fonction des valeurs de la colonne Student_Score pour chaque ligne, mais elle traitera les lignes qui ont la même valeur Student_Score qu'une partition. Vous verrez qu'un numéro unique sera généré pour chaque ligne reflétant son classement Student_Score, en commençant par le numéro 1 sans doublons ni lacunes dans la même partition, réinitialisant le numéro de classement lors du passage à une valeur Student_Score différente.

Par exemple, les étudiants avec un score de 770 seront classés dans ce score en lui attribuant un numéro de classement. Cependant, lorsqu'il est déplacé vers l'étudiant avec un score de 885, le numéro de départ du rang sera réinitialisé pour recommencer à 1, comme indiqué ci-dessous :

CLASSEMENT()

La fonction de fenêtre de classement RANK() renvoie un numéro de classement unique pour chaque ligne distincte dans la partition en fonction d'une valeur de colonne spécifiée, en commençant à 1 pour la première ligne de chaque partition, avec le même rang pour les valeurs en double et en laissant des espaces entre les rangs; cet écart apparaît dans la séquence après les valeurs en double. En d'autres termes, la fonction de fenêtre de classement RANK() se comporte comme la fonction ROW_NUMBER() sauf pour les lignes avec des valeurs égales, où elle se classera avec le même ID de classement et générera un écart après celui-ci. Si nous modifions la requête de classement précédente pour utiliser la fonction de classement RANK() :

SELECT *,  RANK ()  OVER( ORDER BY Student_Score) AS RankRank
FROM StudentScore

Vous verrez d'après le résultat que la fonction de fenêtre RANK classera les lignes du tableau en fonction des valeurs de la colonne Student_Score pour chaque ligne, avec une valeur de classement reflétant son Student_Score à partir du nombre 1, et classant les lignes qui ont le même Student_Score avec le même valeur de rang. Vous pouvez également voir que deux lignes ayant Student_Score égal à 770 sont classées avec la même valeur, laissant un espace, qui est le numéro 2 manqué, après la deuxième ligne classée. La même chose se produit avec les lignes où Student_Score est égal à 1140 qui sont classées avec la même valeur, laissant un espace, qui est le nombre manquant 6, après la deuxième ligne, comme indiqué ci-dessous :

Modification de la requête précédente en incluant la clause PARTITION BY pour avoir plus d'une partition, comme indiqué dans la requête T-SQL ci-dessous :

SELECT *, RANK() OVER(PARTITION BY Student_Score  ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore

Le résultat du classement n'aura aucune signification, car le classement sera effectué en fonction des valeurs Student_Score pour chaque partition, et les données seront partitionnées en fonction des valeurs Student_Score. Et du fait que chaque partition aura des lignes avec les mêmes valeurs Student_Score, les lignes avec les mêmes valeurs Student_Score dans la même partition seront classées avec une valeur égale à 1. Ainsi, lors du passage à la deuxième partition, le rang sera être réinitialisé, en recommençant par le chiffre 1, ayant toutes les valeurs de classement égales à 1 comme indiqué ci-dessous :

DENSE_RANK()

La fonction de fenêtre de classement DENSE_RANK() est similaire à la fonction RANK() en générant un numéro de rang unique pour chaque ligne distincte dans la partition en fonction d'une valeur de colonne spécifiée, en commençant à 1 pour la première ligne de chaque partition, en classant les lignes avec des valeurs égales avec le même numéro de rang, sauf qu'il ne saute aucun rang, ne laissant aucun espace entre les rangs.

Si nous réécrivons la requête de classement précédente pour utiliser la fonction de classement DENSE_RANK() :

Encore une fois, modifiez la requête précédente en incluant la clause PARTITION BY pour avoir plus d'une partition, comme indiqué dans la requête T-SQL ci-dessous :

SELECT *, DENSE_RANK() OVER(PARTITION BY Student_Score  ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore

Les valeurs de classement n'auront aucune signification, où toutes les lignes seront classées avec la valeur 1, en raison de l'attribution des valeurs en double à la même valeur de classement et de la réinitialisation de l'ID de départ du classement lors du traitement d'une nouvelle partition, comme indiqué ci-dessous :

NTILE(N)

La fonction de fenêtre de classement NTILE(N) est utilisée pour répartir les lignes de l'ensemble de lignes dans un nombre spécifié de groupes, en fournissant à chaque ligne de l'ensemble de lignes un numéro de groupe unique, en commençant par le numéro 1 qui indique le groupe auquel cette ligne appartient. à, où N est un nombre positif, qui définit le nombre de groupes dans lesquels vous devez répartir l'ensemble de lignes.

En d'autres termes, si vous devez diviser des lignes de données spécifiques du tableau en 3 groupes, en fonction de valeurs de colonne particulières, la fonction de fenêtre de classement NTILE(3) vous aidera à y parvenir facilement.

Le nombre de rangées dans chaque groupe peut être calculé en divisant le nombre de rangées par le nombre requis de groupes. Si nous modifions la requête de classement précédente pour utiliser la fonction de fenêtre de classement NTILE(4) pour classer sept lignes de table en quatre groupes comme la requête T-SQL ci-dessous :

SELECT *,  NTILE(4) OVER( ORDER BY Student_Score) AS NTILERank
FROM StudentScore

Le nombre de lignes doit être de (7/4 =1,75) lignes dans chaque groupe. À l'aide de la fonction NTILE(), SQL Server Engine attribuera 2 lignes aux trois premiers groupes et une ligne au dernier groupe, afin que toutes les lignes soient incluses dans les groupes, comme indiqué dans le jeu de résultats ci-dessous :

Modification de la requête précédente en incluant la clause PARTITION BY pour avoir plus d'une partition, comme indiqué dans la requête T-SQL ci-dessous :

SELECT *, NTILE(4) OVER(PARTITION BY Student_Score  ORDER BY Student_Score) AS RowNumberRank
FROM StudentScore

Les rangées seront réparties en quatre groupes sur chaque partition. Par exemple, les deux premières lignes avec Student_Score égal à 770 seront dans la même partition et seront distribuées au sein des groupes classant chacun avec un numéro unique, comme indiqué dans le jeu de résultats ci-dessous :

Tout mettre ensemble

Pour avoir un scénario de comparaison plus clair, tronquons le tableau précédent, ajoutons un autre critère de classification, qui est la classe des étudiants, et insérons enfin sept nouvelles lignes à l'aide du script T-SQL ci-dessous :

TRUNCATE TABLE StudentScore
GO
ALTER TABLE StudentScore ADD CLASS CHAR(1)
GO
INSERT INTO StudentScore VALUES (1,'Ali', 978,'A')
INSERT INTO StudentScore VALUES (2,'Zaid', 770,'B')
INSERT INTO StudentScore VALUES (3,'Mohd', 1140,'A')
INSERT INTO StudentScore VALUES (4,'Jack', 879,'B')
INSERT INTO StudentScore VALUES (5,'John', 1240,'C')
INSERT INTO StudentScore VALUES (6,'Mike', 1100,'B')
INSERT INTO StudentScore VALUES (7,'Goerge', 885,'C')

Après cela, nous classerons sept lignes en fonction du score de chaque élève, en partitionnant les élèves en fonction de leur classe. En d'autres termes, chaque partition comprendra une classe, et chaque classe d'étudiants sera classée en fonction de leurs scores au sein de la même classe, en utilisant quatre fonctions de fenêtre de classement décrites précédemment, comme indiqué dans le script T-SQL ci-dessous :

SELECT *, ROW_NUMBER() OVER(PARTITION BY CLASS ORDER BY Student_Score) AS RowNumberRank,
          RANK ()  OVER(PARTITION BY CLASS ORDER BY Student_Score) AS RankRank,
		  DENSE_RANK ()  OVER(PARTITION BY CLASS ORDER BY Student_Score) AS DenseRankRank,
		  NTILE(7) OVER(PARTITION BY CLASS ORDER BY Student_Score) AS NTILERank

FROM StudentScore
GO

En raison du fait qu'il n'y a pas de valeurs en double, quatre fonctions de fenêtre de classement fonctionneront de la même manière, renvoyant le même résultat, comme indiqué dans le jeu de résultats ci-dessous :

Si un autre élève est inclus dans la classe A avec un score, qu'un autre élève de la même classe a déjà, en utilisant l'instruction INSERT ci-dessous :

INSERT INTO StudentScore VALUES (8,'Faisal', 978,'A')

Rien ne changera pour les fonctions de fenêtre de classement ROW_NUMBER() et NTILE(). Les fonctions RANK et DENSE_RANK() attribueront le même rang aux étudiants ayant le même score, avec un écart dans les rangs après les rangs en double lors de l'utilisation de la fonction RANK et aucun écart dans les rangs après les rangs en double lors de l'utilisation de DENSE_RANK( ), comme indiqué dans le résultat ci-dessous :

Scénario pratique

Les fonctions de fenêtre de classement sont largement utilisées par les développeurs SQL Server. L'un des scénarios courants pour l'utilisation des fonctions de classement, lorsque vous souhaitez extraire des lignes spécifiques et en ignorer d'autres, en utilisant la fonction de fenêtre de classement ROW_NUMBER (,) dans un CTE, comme dans le script T-SQL ci-dessous qui renvoie les étudiants avec des rangs entre 2 et 5 et sautez les autres :

WITH ClassRanks AS
(
  SELECT *, ROW_NUMBER() OVER( ORDER BY Student_Score) AS RowNumberRank
	FROM StudentScore
)

SELECT Student_Name , Student_Score 
FROM ClassRanks
WHERE RowNumberRank >= 2 and RowNumberRank <=5
ORDER BY RowNumberRank

Le résultat montrera que seuls les étudiants dont les rangs sont compris entre 2 et 5 seront renvoyés :

À partir de SQL Server 2012, une nouvelle commande utile, OFFSET FETCH a été introduit qui peut être utilisé pour effectuer la même tâche précédente en récupérant des enregistrements spécifiques et en sautant les autres, en utilisant le script T-SQL ci-dessous :

WITH ClassRanks AS
(
  SELECT *, ROW_NUMBER() OVER( ORDER BY Student_Score) AS RowNumberRank
	FROM StudentScore
)

SELECT Student_Name , Student_Score 
FROM ClassRanks
ORDER BY
      RowNumberRank OFFSET 1 ROWS FETCH NEXT 4 ROWS ONLY;

Récupération du même résultat précédent comme indiqué ci-dessous :

Conclusion

SQL Server nous fournit quatre fonctions de fenêtre de classement qui nous aident à classer l'ensemble de lignes fourni en fonction de valeurs de colonne spécifiques. Ces fonctions sont :ROW_NUMBER(), RANK(), DENSE_RANK() et NTILE(). Toutes ces fonctions de classement effectuent la tâche de classement à leur manière, renvoyant le même résultat lorsqu'il n'y a pas de valeurs en double dans les lignes. S'il y a une valeur en double dans l'ensemble de lignes, la fonction RANK attribuera le même ID de classement pour toutes les lignes avec la même valeur, laissant des espaces entre les rangs après les doublons. La fonction DENSE_RANK attribuera également le même ID de classement pour toutes les lignes avec la même valeur, mais ne laissera aucun espace entre les rangs après les doublons. Nous passons en revue différents scénarios dans cet article pour couvrir tous les cas possibles qui vous aident à comprendre concrètement les fonctions de la fenêtre de classement.

Références :

  • ROW_NUMBER (Transact SQL)
  • CLASSEMENT (Transact-SQL)
  • DENSE_RANK (Transact SQL)
  • NTILE (Transact-SQL)
  • Clause OFFSET FETCH (SQL Server Compact)