18th Jul 2022 Tempo di lettura: 15 minuti Come classificare le righe in SQL: Guida completa Kamil Bladoszewski sql imparare sql rank esercizi online Indice Le funzioni di classificazione SQL Window Functions Funzioni di classificazione di base RANK() vs. DENSE_RANK() vs. ROW_NUMBER() Casi d'uso della classificazione SQL Classifica su una singola colonna Classifica su più colonne RANK() con i primi 10 risultati Classificazione per data Ordinamento per mese Classificazione con GROUP BY Classifiche con COUNT() Classifica con SUM() RANK() OVER(PARTITION BY ...)-Colonna singola RANK() OVER(PARTITION BY ...)-Colonne multiple Ricordate: la pratica rende perfetti Non dovrete più lottare con le funzioni di ranking di SQL! Questo articolo vi guiderà attraverso i casi d'uso più comuni delle funzioni di ranking. Le classifiche sono utilizzate ogni giorno. Tornei sportivi, i migliori film e serie TV su Netflix, negozi con i prodotti più economici: questi sono solo alcuni esempi di classifiche che potreste aver visto di recente. L'uso delle funzioni SQL RANK può essere talvolta difficile. La varietà di costruzioni diverse è enorme. È facile perdersi in tutto ciò che c'è da imparare sulle classifiche. La chiave è capire i concetti importanti e sapere dove cercare ulteriori informazioni. In questa guida troverete le conoscenze che vi permetteranno di scrivere molti tipi di query di ranking SQL. Innanzitutto, spiegherò come funziona il ranking. Poi mostrerò una serie di esempi. Se cercate casi d'uso specifici, potete semplicemente immergervi nella sezione Casi d'uso del ranking SQL. Le funzioni di classificazione SQL Window Functions Iniziamo dalle basi. Cosa sono esattamente le funzioni di ranking in SQL? Fanno parte di una famiglia di funzioni chiamate funzioni finestra. Le funzioni finestra utilizzano un ambito (finestra) che esamina una parte dei dati per calcolare il risultato. Poi si spostano su un'altra parte dei dati e calcolano il risultato per quella parte. Un'idea di base del suo funzionamento si può ricavare da SQL Window Function Example With Explanations. Sebbene esistano molte funzioni di finestra diverse, mi concentrerò solo sulla classificazione. Inoltre, non spiegherò a fondo il funzionamento interno delle funzioni finestra. Questo articolo si concentrerà sulle funzioni di classificazione di SQL e su come utilizzarle in diverse situazioni. Per saperne di più sulle funzioni finestra, consultate questo Window Functions corso. Funzioni di classificazione di base I tipi più comuni (e semplici) di funzioni di classificazione sono: RANK() DENSE_RANK() ROW_NUMBER() Che cosa fanno e come si differenziano l'una dall'altra? Spiegherò brevemente ciascuna di esse. Poi confronterò le classifiche di ciascuna funzione. La funzione RANK() crea una classifica delle righe in base a una colonna fornita. Inizia con l'assegnare "1" alla prima riga dell'ordine e poi assegna numeri più alti alle righe inferiori. Se le righe hanno lo stesso valore, vengono classificate allo stesso modo. Tuttavia, il posto successivo viene spostato di conseguenza. Ad esempio, se due righe sono al 5° posto (hanno lo stesso rango), la riga successiva sarà la 7° (cioè la 6° non esiste). La funzione DENSE_RANK() è piuttosto simile. L'unica differenza è che non lascia spazi vuoti nella classifica. Anche se più di una riga può avere lo stesso rango, la riga successiva otterrà il rango successivo. Ad esempio, se due righe sono al 5° posto, la riga successiva sarà al 6°. La funzione ROW_NUMBER() è diversa. Se le righe hanno lo stesso valore, otterranno tutte una classifica consecutiva (non la stessa classifica come con le funzioni precedenti). Ad esempio, se due righe hanno lo stesso valore (sarebbero entrambe al 5° posto con le funzioni precedenti), ROW_NUMBER() le collocherà al 5° e al 6° posto. Ora ci si potrebbe chiedere: Se alcune righe hanno lo stesso valore e si usa ROW_NUMBER(), come si fa a sapere quale riga sarà la prima, la seconda, ecc. La risposta è piuttosto cupa: non si può! Dipende da molti fattori e non è possibile prevedere quale riga otterrà un determinato punteggio. Queste sono le funzioni di classificazione SQL di base e (probabilmente) più utilizzate. Se volete conoscere altre funzioni di classificazione, potete leggere questa panoramica delle funzioni di classificazione. RANK() vs. DENSE_RANK() vs. ROW_NUMBER() Ora che conoscete la teoria delle funzioni di classificazione, confrontiamo i risultati di queste funzioni. Per farlo, classifichiamo alcune persone in base ai loro punteggi nei test. Osserviamo i dati (tabella exam_result): first_namelast_namepoints EmyrDownes70 DinaMorin70 Evie-MayBoyer80 NoraParkinson60 TrystanOconnor40 ErykMyers90 MarleneDuncan90 MariusPowell90 JoanneGoddard50 RayhanWilliamson90 Creeremo una query che classifica le righe in base alla colonna dei punti, utilizzando le funzioni di classificazione descritte in precedenza: SELECT RANK() OVER(ORDER BY points DESC) AS rank, DENSE_RANK() OVER(ORDER BY points DESC) AS dense_rank, ROW_NUMBER() OVER(ORDER BY points DESC) AS row_number, first_name, last_name, points FROM exam_result; Analizziamo il codice prima di eseguirlo per vedere i risultati. La prima colonna che vogliamo mostrare è rank. Sarà semplicemente la classifica creata con la funzione RANK(). Ma cosa significa OVER(ORDER BY points DESC)? Fa parte del concetto di funzione finestra. OVER() è una parte obbligatoria di tutte le funzioni di classificazione (in realtà, di tutte le funzioni finestra). Indica alla funzione l'ambito dei dati (la finestra) e l'ordine in cui saranno collocate le righe. In questo caso, abbiamo fornito solo l'ordine. Un esempio di definizione di una finestra si trova nella sezione RANK() OVER(PARTITION BY ...)-Single Column. Poiché ora si è compresa la prima colonna, si dovrebbero comprendere anche le due successive. L'unica differenza è il nome delle funzioni di classificazione. Inoltre, le ultime tre colonne dovrebbero essere autoesplicative, dato che i dati sono già stati visti. Bene, diamo un'occhiata ai risultati: rankdense_rankrow_numberfirst_namelast_namepoints 111MarleneDuncan90 112RayhanWilliamson90 113MariusPowell90 114ErykMyers90 525Evie-MayBoyer80 636EmyrDownes70 637DinaMorin70 848NoraParkinson60 959JoanneGoddard50 10610TrystanOconnor40 Riuscite a individuare le differenze tra le varie funzioni? Per le prime righe, le colonne rank e dense_rank sembrano uguali. Tuttavia, row_number appare diversa fin dall'inizio. Anche se Marlene, Rayhan, Marius e Eryk hanno la stessa quantità di punti, la funzione ROW_NUMBER() ha assegnato loro valori diversi. Come detto in precedenza, l'ordine in cui queste persone hanno ottenuto il loro rango non è deterministico. Vale la pena notare che è possibile rendere il risultato di ROW_NUMBER() più deterministico. È sufficiente aggiungere altre colonne all'ordine (per esempio, ROW_NUMBER() OVER (ORDER BY points DESC, last_name ASC)). Questo verrà spiegato ulteriormente nella sezione Classifica su più colonne. È anche possibile rendere ROW_NUMBER() ancora meno deterministico! Anche se la parte ORDER BY è necessaria per RANK() e DENSE_RANK(), ROW_NUMBER() non la richiede affatto. In questo modo è possibile numerare le righe senza un ordine specifico. Un'altra differenza si può notare più in basso nella tabella. Evie-May è classificata al 5° posto dalla funzione RANK() e al 2° posto dalla funzione DENSE_RANK(). La prima funzione prende nota di quante righe avevano la stessa posizione e la riga successiva viene classificata di conseguenza. La seconda funzione prende semplicemente in considerazione il valore precedente e alla riga successiva viene assegnata la classifica successiva. Guardate le classifiche qui sopra e assicuratevi di capire la differenza tra queste tre funzioni di classificazione SQL. È importante sapere quale funzione utilizzare in quali casi. Se avete bisogno di ulteriori spiegazioni, date un'occhiata a Come usare le funzioni di classificazione in SQL. Casi d'uso della classificazione SQL È ora di immergersi in alcune query SQL RANK. Inizieremo con alcune query semplici e aumenteremo gradualmente la complessità. Lo schema è il seguente: Classifica di base: Classifica su una singola colonna Classifica su più colonne RANK() con i primi 10 risultati Classifica con date: Classifica per data Classifica per mese Classificazione con funzioni aggregate: Classifica con GROUP BY Classifica con COUNT() Classifica con SUM() Utilizzo della partizione per: RANK() OVER(PARTITION BY ...)-Singola colonna RANK() OVER(PARTITION BY ...)-Colonne multiple Anche se utilizzeremo principalmente la funzione SQL RANK(), gli esempi possono essere applicati a DENSE_RANK() e ROW_NUMBER(). Dipende dalle vostre esigenze. È quindi importante capire le differenze tra queste funzioni. Negli esempi, utilizzeremo i dati modificati dell'esempio precedente. La colonna exam_date mostra quando la persona ha sostenuto l'esame e la colonna città mostra in quale city ha sostenuto l'esame. Date un'occhiata: first_namelast_nameexam_datecitypoints EmyrDownes2018-12-18San Francisco70 DinaMorin2019-01-17Los Angeles70 Evie-MayBoyer2019-01-23San Francisco80 NoraParkinson2019-02-16San Diego60 TrystanOconnor2019-02-28Los Angeles40 ErykMyers2019-06-07San Francisco90 MarleneDuncan2019-06-13San Diego90 MariusPowell2019-11-13San Diego90 JoanneGoddard2019-12-18San Diego50 MariusWilliamson2020-01-02San Diego90 Classifica su una singola colonna Inizieremo con il caso d'uso più semplice: creare una classifica basata su una sola colonna. Baseremo la nostra classifica sui punti: SELECT RANK() OVER(ORDER BY points DESC) AS ranking, first_name, last_name, points FROM exam_result; La query è piuttosto semplice, come l'esempio precedente. Crea una classifica basata sul numero di punti in ordine decrescente. Utilizziamo RANK() OVER(ORDER BY ...) per indicare quale colonna deve essere utilizzata per l'ordinamento. I risultati sono questi: rankingfirst_namelast_namepoints 1MarleneDuncan90 1MariusWilliamson90 1MariusPowell90 1ErykMyers90 5Evie-MayBoyer80 6EmyrDownes70 6DinaMorin70 8NoraParkinson60 9JoanneGoddard50 10TrystanOconnor40 Se si desidera creare una classifica basata su una colonna diversa o su un ordine diverso (ad esempio, ascendente anziché discendente), è sufficiente cambiare il nome della colonna o cambiare la parola chiave DESC con la parola chiave ASC. Classifica su più colonne Anche l'uso di più colonne per l'ordinamento è semplice. Basta aggiungere il nome della colonna successiva dopo la virgola. Se i valori della prima colonna sono uguali, viene presa in considerazione la seconda colonna e così via. Guardate: SELECT RANK() OVER(ORDER BY points DESC, first_name ASC) AS ranking, first_name, last_name, points FROM exam_result; Questa query è simile alla precedente. Abbiamo aggiunto first_name ASC alla clausola di ordinamento. In questo modo, se alcune righe hanno lo stesso valore nella prima colonna, viene presa in considerazione la seconda. Si noti che se la seconda colonna è uguale, la classificazione viene risolta in base alla funzione utilizzata (nel nostro caso RANK()). Osservate il risultato: rankingfirst_namelast_namepoints 1ErykMyers90 2MariusWilliamson90 2MariusPowell90 4MarleneDuncan90 5Evie-MayBoyer80 6DinaMorin70 7EmyrDownes70 8NoraParkinson60 9JoanneGoddard50 10TrystanOconnor40 Come si può vedere, le persone con lo stesso numero di punti hanno classifiche diverse perché hanno nomi diversi. Le uniche eccezioni sono le due persone di nome Marius. Poiché hanno lo stesso nome e lo stesso numero di punti, hanno la stessa posizione. Vale la pena ricordare che l'ordinamento per alcune colonne può essere utile se si vuole rendere deterministico il risultato della funzione ROW_NUMBER(). Osservate questa query: SELECT ROW_NUMBER() OVER(ORDER BY points DESC, last_name ASC) AS ranking, first_name, last_name, points FROM exam_result; Poiché tutti i nostri dati hanno un cognome diverso, possiamo aggiungerlo alla clausola di ordinamento in modo da poter prevedere chi ottiene quale rango. Guardate: rankingfirst_namelast_namepoints 1MarleneDuncan90 2ErykMyers90 3MariusPowell90 4MariusWilliamson90 5Evie-MayBoyer80 6EmyrDownes70 7DinaMorin70 8NoraParkinson60 9JoanneGoddard50 10TrystanOconnor40 In questo caso, la funzione ROW_NUMBER() funziona come RANK() e DENSE_RANK() perché ogni riga può essere posizionata in modo deterministico. Tuttavia, si noti che se ci fossero persone con lo stesso cognome, la funzione ROW_NUMBER() non sarebbe deterministica, anche se stiamo ordinando per due colonne. RANK() con i primi 10 risultati Spesso si creano classifiche per mostrare i risultati più importanti (ad esempio, i primi 10, i primi 100, ecc.). Come ci si potrebbe aspettare, è possibile mostrare i primi risultati utilizzando le funzioni di classificazione SQL. Una query di questo tipo, però, è un po' più complicata. Guardate un po': SELECT * FROM ( SELECT RANK() OVER(ORDER BY points DESC) AS ranking, first_name, last_name, points FROM exam_result ) AS a WHERE ranking <= 3; In questa query, utilizziamo una sottoquery per calcolare la classifica e poi, nella query principale, filtriamo i risultati in modo da mostrare solo i primi tre. I risultati potrebbero sorprendervi: rankingfirst_namelast_namepoints 1MarleneDuncan90 1MariusWilliamson90 1MariusPowell90 1ErykMyers90 Come si può vedere, i risultati mostrano quattro righe e ognuna di esse ha lo stesso valore nella colonna della classifica. Questo è dovuto ai nostri dati. Quattro righe hanno lo stesso numero di punti e quindi ottengono la stessa posizione nella sottoquery. Per scrivere una query di questo tipo, è sufficiente inserire la query di ranking all'interno di una subquery e scrivere un filtro intorno ad essa. Ad esempio, per mostrare i 10, cambiare WHERE ranking <= 3 in WHERE ranking <= 10. Vale anche la pena di notare cosa succede quando si usa DENSE_RANK() con questi dati. Osservate la query seguente e i suoi risultati: SELECT * FROM ( SELECT DENSE_RANK() OVER(ORDER BY points DESC) AS ranking, first_name, last_name, points FROM exam_result ) AS a WHERE ranking <= 3; rankingfirst_namelast_namepoints 1MarleneDuncan90 1MariusWilliamson90 1MariusPowell90 1ErykMyers90 2Evie-MayBoyer80 3EmyrDownes70 3DinaMorin70 Come si può vedere, ci sono molte righe nel risultato! Ancora una volta, ciò è dovuto al modo in cui DENSE_RANK() classifica ogni riga. Questo esempio mostra chiaramente che è importante capire la differenza tra le varie funzioni e sapere quale utilizzare in una determinata situazione. Classificazione per data SELECT RANK() OVER(ORDER BY exam_date ASC) AS ranking, first_name, last_name, exam_date FROM exam_result; In questo caso, stiamo semplicemente usando la colonna exam_date per ordinare. La differenza tra l'ordinamento per data e quello per qualsiasi altra colonna è minima. Osservate i risultati: rankingfirst_namelast_nameexam_date 1EmyrDownes2018-12-18 2DinaMorin2019-01-17 3Evie-MayBoyer2019-01-23 4NoraParkinson2019-02-16 5TrystanOconnor2019-02-28 6ErykMyers2019-06-07 7MarleneDuncan2019-06-13 8MariusPowell2019-11-13 9JoanneGoddard2019-12-18 10MariusWilliamson2020-01-02 La cosa importante da ricordare è che ASC (ascendente) nel caso delle date significa che il più vecchio sarà posizionato per primo. Nell'ordine DESC (discendente), la data più recente sarà la prima. Ordinamento per mese È possibile ordinare le righe utilizzando solo una parte della data (ad esempio, il mese). Non è difficile se si conoscono le funzioni della data. Guardate la seguente query: SELECT RANK() OVER(ORDER BY EXTRACT('year' FROM exam_date) ASC, EXTRACT('month' FROM exam_date) ASC) AS ranking, first_name, last_name, exam_date FROM exam_result; Utilizziamo la funzione EXTRACT() per estrarre una determinata parte della data. Per prima cosa, vogliamo ordinare le righe per anno e poi per mese. Quindi, utilizziamo prima EXTRACT('year' FROM exam_date) e poi EXTRACT('month' FROM exam_date). Osservate i risultati: rankingfirst_namelast_nameexam_date 1EmyrDownes2018-12-18 2Evie-MayBoyer2019-01-23 2DinaMorin2019-01-17 4NoraParkinson2019-02-16 4TrystanOconnor2019-02-28 6MarleneDuncan2019-06-13 6ErykMyers2019-06-07 8MariusPowell2019-11-13 9JoanneGoddard2019-12-18 10MariusWilliamson2020-01-02 EXTRACT() è una funzione piuttosto semplice. Si dice quale parte di data si vuole estrarre dalla data. Ci sono molte parti di data diverse che si possono usare. È meglio consultare la documentazione del proprio sistema di gestione dei database (DBMS). Le parti più comuni sono: 'year' 'month' 'day' 'hour' 'minute' 'second' È bene ricordare che non tutti i DBMS supportano questa funzione. Quelli che la supportano sono PostgreSQL e MySQL. In SQL Server, questa funzione si chiama DATEPART(). Le operazioni con la data e l'ora sono un argomento piuttosto complesso, che esula dallo scopo di questo articolo. Se volete saperne di più sulla funzione EXTRACT() e su molte altre, date un'occhiata al corso Standard SQL Functions . C'è un'intera sezione dedicata alle operazioni su data e ora. Classificazione con GROUP BY Ora vedremo come utilizzare le classifiche con le funzioni aggregate. Anche se sembra spaventoso, con la giusta comprensione, è un concetto piuttosto logico. Il database calcola prima le funzioni aggregate e poi crea una classifica basata sui valori calcolati. Guardate questo esempio con AVG(): SELECT RANK() OVER(ORDER BY AVG(points) DESC) AS ranking, city, AVG(points) AS average_points FROM exam_result GROUP BY city; Come si può vedere, questa query non è molto diversa dalle altre query che abbiamo visto finora. Si possono semplicemente usare funzioni aggregate all'interno di funzioni di classificazione. La cosa importante da ricordare è l'uso della clausola GROUP BY. Come già detto, le funzioni aggregate vengono calcolate per prime. Ciò significa che con GROUP BY si possono usare solo le funzioni aggregate o le espressioni che si stanno raggruppando all'interno della funzione di classificazione. Ad esempio, se si volesse utilizzare un'altra colonna per l'ordinamento, in modo che le righe siano ordinate in base a quest'altra colonna se il numero medio di punti è lo stesso, si dovrebbe includere quest'altra colonna nella clausola GROUP BY. La query precedente restituisce il numero medio di punti ottenuti dalle persone di ogni città. rankingcityaverage_points 1San Francisco80 2San Diego76 3Los Angeles55 Classifiche con COUNT() Ora daremo un'occhiata a una funzione aggregata spesso utilizzata:COUNT(): SELECT RANK() OVER(ORDER BY COUNT(*) DESC) AS ranking, city, COUNT(*) AS exam_takers FROM exam_result GROUP BY city; Come si può vedere, questa query non è molto diversa da quella precedente. Tutte le funzioni aggregate sono utilizzate allo stesso modo con le funzioni di classificazione. I risultati sono i seguenti: rankingcityexam_takers 1San Diego5 2San Francisco3 3Los Angeles2 La query calcola il numero di persone che hanno sostenuto l'esame in ogni città e crea una classifica basata su questo valore. Classifica con SUM() Un'altra funzione aggregata molto diffusa è SUM(). Anch'essa è piuttosto semplice da usare: SELECT RANK() OVER(ORDER BY SUM(points) DESC) AS ranking, city, SUM(points) AS total_points FROM exam_result GROUP BY city; Ancora una volta, utilizziamo semplicemente la funzione SUM() con la funzione SQL RANK(). rankingcitytotal_points 1San Diego380 2San Francisco240 3Los Angeles110 Questa volta, abbiamo calcolato il numero totale di punti acquisiti dalle persone di ogni città. RANK() OVER(PARTITION BY ...)-Colonna singola Spero che siate pronti ad affrontare un concetto di classifica più avanzato: PARTITION BY. Permette di creare classifiche in gruppi separati. Date un'occhiata a questa query: SELECT RANK() OVER(PARTITION BY city ORDER BY points DESC) AS ranking, city, first_name, last_name, points FROM exam_result; In questa query, utilizziamo PARTITION BY con una singola colonna per creare una classifica delle persone in ogni città. In questo modo, possiamo vedere le persone con i punteggi più alti in ogni città. Guardate i risultati: rankingcityfirst_namelast_namepoints 1San FranciscoErykMyers90 2San FranciscoEvie-MayBoyer80 3San FranciscoEmyrDowes70 1Los AngelesDinaMorin70 2Los AngelesTrystanOconnor40 1San DiegoMarleneDuncan90 1San DiegoMariusPowell90 1San DiegoMariusWilliamson90 4San DiegoNoraParkinson60 5San DiegoJoanneGoddard50 Come si può notare, le classifiche sono calcolate separatamente per ogni città. In questo modo, Eryk e Dina ottengono la stessa classifica, anche se hanno ottenuto punteggi diversi. Inoltre, Dina ed Emyr hanno gli stessi punteggi, ma Dina è classificata più in alto perché ha sostenuto l'esame in una città diversa. In alcuni casi, PARTITION BY è un concetto importante e quindi vale la pena ricordarlo. L'aspetto positivo è che è anche piuttosto semplice da usare. Tuttavia, se volete maggiori informazioni, potete leggere Common SQL Window Functions: Using Partitions With Ranking Functions sul blog LearnSQL.it . RANK() OVER(PARTITION BY ...)-Colonne multiple Infine, analizziamo la clausola PARTITION BY con più colonne. Non è molto diverso dall'uso di PARTITION BY con una sola colonna. Guardate: SELECT RANK() OVER(PARTITION BY city, first_name ORDER BY exam_date ASC) AS ranking, city, first_name, last_name, exam_date FROM exam_result; Nella query precedente, stiamo usando PARTITION BY con due colonne: city e first_name. Ciò significa che per ogni coppia distinta di città e nome, avremo classifiche separate. Osservate i risultati: rankingcityfirst_namelast_nameexam_date 1San FranciscoErykMyers2019-06-07 1San FranciscoEvie-MayBoyer2019-01-23 1San FranciscoEmyrDowes2018-12-18 1Los AngelesDinaMorin2019-01-17 1Los AngelesTrystanOconnor2019-02-28 1San DiegoMarleneDuncan2019-06-13 1San DiegoMariusPowell2019-11-13 2San DiegoMariusWilliamson2020-01-02 1San DiegoNoraParkinson2019-02-16 1San DiegoJoanneGoddard2019-12-18 Come si può vedere, la maggior parte delle persone è classificata al primo posto. Questo perché la maggior parte delle coppie (città e nome) sono uniche. Tuttavia, c'è una coppia che non è unica. Ci sono due persone di San Diego che si chiamano Marius. Marius Powell è il primo perché ha sostenuto l'esame prima di Marius Williamson. Ricordate: la pratica rende perfetti Come si può notare, i casi di utilizzo delle funzioni di classificazione in SQL sono numerosi. È quindi importante conoscerle bene: probabilmente prima o poi dovrete scrivere una query di classificazione in SQL. Il modo migliore per imparare le funzioni di classificazione (e le funzioni delle finestre in generale) è la pratica. Consiglio questo Window Functions corso. Ha 218 esercizi interattivi, che equivalgono a circa 20 ore di codifica. È un bel po', soprattutto se si decide di fare tutto d'un fiato. Noi non lo consigliamo. È meglio distribuire lo studio su più giorni. Qui troverete altri consigli su come mantenersi in salute durante l'apprendimento di SQL. Prendetevi cura del vostro corpo mentre sviluppate la vostra carriera e iniziate a imparare SQL oggi stesso. Tags: sql imparare sql rank esercizi online