Righe Top-N per gruppo con ROW_NUMBER
Il pattern classico di partizionamento e classificazione per ottenere le prime 3 righe per categoria
Righe Top-N per gruppo con ROW_NUMBER è una lezione SQL Interview Prep gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento SQL Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso SQL Interview Prep include 4 lezioni in totale.
La domanda sul top-N per gruppo
Una delle domande più comuni nei colloqui SQL sembra semplice: "Restituisca i 3 dipendenti con lo stipendio più alto in ogni reparto." I candidati che ricorrono subito a LIMIT sbagliano, perché LIMIT limita l'intero set di risultati, non ogni gruppo.
L'intervistatore verifica se conosce le funzioni finestra. La risposta canonica è: numerare le righe all'interno di ogni gruppo, quindi mantenere le righe il cui numero è ≤ N. Questa lezione costruisce questo schema passo dopo passo.
Perché LIMIT non può risolvere il problema
Supponga di scrivere la query seguente. Restituisce solo 3 righe in totale nell'intera tabella, non 3 per reparto.
LIMIT (oppure TOP o FETCH FIRST) opera sul set di risultati finale. In SQL standard non esiste un LIMIT per gruppo. Quando in un colloquio propone LIMIT 3 per un problema relativo ai gruppi, dimostra di non aver ancora assimilato il concetto di partizionamento.
-- WRONG: only 3 rows total, not 3 per department
SELECT department, name, salary
FROM employees
ORDER BY salary DESC
LIMIT 3;Presentiamo ROW_NUMBER
ROW_NUMBER() è una funzione finestra che assegna a ogni riga un intero univoco e senza interruzioni in base a un ordinamento. Utilizzata da sola, numera l'intero risultato.
L'elemento decisivo è PARTITION BY: riavvia la numerazione da 1 per ogni gruppo. Combini PARTITION BY department con ORDER BY salary DESC e ogni reparto riceve la propria sequenza 1, 2, 3, ... in base allo stipendio.
SELECT
name,
department,
salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS rn
FROM employees;Come leggere l'output numerato
Dopo aver eseguito la query precedente, ogni riga contiene un valore rn. All'interno di ogni reparto, lo stipendio più alto riceve rn = 1, quello successivo 2 e così via. Quando inizia un nuovo reparto, la numerazione riparte da 1.
- Vendite: Ana (1), Bo (2), Cal (3), Dee (4)
- Ingegneria: Eve (1), Fin (2), Gus (3)
Ora "top 3 per reparto" significa semplicemente "mantenere le righe in cui rn <= 3".
Non può filtrare rn in WHERE
Il passo successivo più naturale è WHERE rn <= 3, ma non funziona. Le funzioni finestra vengono calcolate dopo la clausola WHERE nell'ordine logico di esecuzione, quindi l'alias rn non esiste ancora quando viene eseguito WHERE.
Gli intervistatori amano questa insidia. La soluzione consiste nel calcolare la funzione finestra in una sottoquery o CTE, quindi filtrare il risultato di quella query interna in una query esterna.
-- ERROR: rn does not exist in WHERE
SELECT name, department, salary,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
WHERE rn <= 3;La soluzione canonica con una CTE
Racchiuda la numerazione in una CTE denominata ranked, quindi selezioni i dati da essa applicando il filtro nel WHERE della query esterna. È la risposta che gli intervistatori vogliono vedere ed è anche molto leggibile.
Memorizzi questo schema: partizionare per gruppo, ordinare per metrica, filtrare rn ≤ N nella query esterna. Si generalizza a top-1, top-5 o a qualsiasi N modificando un solo numero.
WITH ranked AS (
SELECT
name, department, salary,
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT name, department, salary
FROM ranked
WHERE rn <= 3
ORDER BY department, rn;La forma con sottoquery
Se il dialetto usato dall'intervistatore è meno recente o preferisce le sottoquery, la stessa logica può essere inserita in una tabella derivata nella clausola FROM. Ricordi che una tabella derivata deve avere un alias (r in questo caso), altrimenti si verifica un errore di sintassi.
Le forme con CTE e con tabella derivata sono intercambiabili per questo problema. Scelga quella che l'intervistatore ritiene più leggibile: sono entrambe corrette.
SELECT name, department, salary
FROM (
SELECT name, department, salary,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
) AS r
WHERE rn <= 3;Top-1: il migliore di ogni gruppo
"Trovi il dipendente con lo stipendio più alto in ogni reparto" equivale semplicemente a N = 1. Imposti il filtro su rn = 1.
Perché non usare MAX(salary) con GROUP BY department? Perché MAX restituisce il valore dello stipendio, ma non il resto della riga di quel dipendente, come nome, data di assunzione e così via. ROW_NUMBER mantiene intatta l'intera riga vincente, che di solito è ciò che la domanda richiede realmente.
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT name, department, salary, hire_date
FROM ranked
WHERE rn = 1;Aggiungere un criterio di spareggio deterministico
ROW_NUMBER restituisce sempre esattamente N righe, anche quando gli stipendi sono uguali. Tuttavia, quale riga a pari merito riceva rn = 1 è arbitrario se non si risolve il pari merito. Se due persone guadagnano 90000 e si mantiene solo rn = 1, la riga scelta può variare tra un'esecuzione e l'altra.
Aggiunga una chiave di ordinamento secondaria e univoca, ad esempio employee_id, per rendere il risultato stabile e riproducibile. Gli intervistatori apprezzano chi menziona spontaneamente il determinismo.
ROW_NUMBER() OVER (
PARTITION BY department
ORDER BY salary DESC, employee_id ASC
) AS rnUn esempio concreto svolto
Data una tabella sales con region, product e revenue, restituisca i 2 prodotti con il fatturato più alto per regione. La procedura è la stessa: partizionare per region, ordinare per revenue DESC e mantenere rn <= 2.
Noti che cambiano solo la colonna di partizione e la colonna della metrica. La struttura è identica, indipendentemente dal settore.
WITH ranked AS (
SELECT region, product, revenue,
ROW_NUMBER() OVER (
PARTITION BY region ORDER BY revenue DESC, product
) AS rn
FROM sales
)
SELECT region, product, revenue
FROM ranked
WHERE rn <= 2
ORDER BY region, rn;Prestazioni e punti da esporre
Per dimostrare una preparazione che va oltre la correttezza, menzioni:
- Un indice su
(department, salary DESC)aiuta il motore a produrre in modo efficiente righe ordinate per partizione. - L'approccio con funzioni finestra esegue una sola scansione della tabella, risultando molto più efficiente di una sottoquery correlata eseguita per ogni riga.
- Per casi top-N molto grandi con N = 1, alcuni motori supportano
DISTINCT ON(Postgres) come scorciatoia, maROW_NUMBERè lo standard portabile.
Dichiari sempre il criterio di spareggio e verifichi il valore di N richiesto.
Verifica rapida
Verifichi la Sua comprensione dello schema top-N per gruppo.
Riepilogo: top-N per gruppo
Lo schema in una frase: partizionare per gruppo, ordinare per metrica, assegnare ROW_NUMBER, quindi mantenere rn ≤ N in una query esterna.
LIMITlimita l'intero insieme, mai i singoli gruppi.- Non può filtrare l'alias della funzione finestra in
WHERE; lo racchiuda in una CTE o sottoquery. - Aggiunga un criterio di spareggio univoco per ottenere risultati deterministici.
- Top-1 mantiene l'intera riga vincente, a differenza di
MAX+GROUP BY.
Modificando un solo numero, la stessa query risolve i casi top-1, top-5 o qualsiasi N.
Domande Frequenti
La lezione «Righe Top-N per gruppo con ROW_NUMBER» è gratuita?
Sì — il testo completo di «Righe Top-N per gruppo con ROW_NUMBER» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso SQL Interview Prep, passa a CoddyKit PRO. Il corso SQL Interview Prep include 4 lezioni in totale.
Cosa imparerò in «Righe Top-N per gruppo con ROW_NUMBER»?
Il pattern classico di partizionamento e classificazione per ottenere le prime 3 righe per categoria Eserciti SQL Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare SQL Interview Prep?
Non è richiesta alcuna esperienza precedente. SQL Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.
Quanto tempo richiede la lezione «Righe Top-N per gruppo con ROW_NUMBER»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione SQL Interview Prep?
Sì. Ogni lezione SQL Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Righe Top-N per gruppo con ROW_NUMBER
- Gestire le parità nelle Top-N
- Deduplicare le righe in sicurezza
- Conservare l'ultima riga per chiave