17th Mar 2023 Tempo di lettura: 24 minuti 25 esempi di query SQL avanzate Ignacio L. Bisso sql imparare sql esercizi online advanced sql Indice 25 esempi di query Advanced SQL con spiegazioni Esempio n. 1 - Classificare le righe in base a uno specifico criterio di ordinamento Esempio n. 2 - Elencare le prime 5 righe di un insieme di risultati Esempio n. 3 - Elencare le ultime 5 righe di un insieme di risultati Esempio 4 - Elencare la seconda riga più alta di un set di risultati Esempio #5 - Elencare il secondo stipendio più alto per reparto Esempio #6 - Elencare il primo 50% di righe in un insieme di risultati Esempio #7 - Elencare l'ultimo 25% di righe in un insieme di risultati Esempio #8 - Numerare le righe in un insieme di risultati Esempio n. 9 - Elenco di tutte le combinazioni di righe di due tabelle Esempio n. 10 - Unire una tabella a se stessa Esempio #11 - Mostrare tutte le righe con un valore superiore alla media Esempio n. 12 - Dipendenti con stipendio superiore alla media del loro dipartimento Esempio n. 13 - Ottenere tutte le righe in cui è presente un valore nel risultato di una sottoquery Esempio #14 - Trovare righe duplicate in SQL Esempio #15 - Conteggio delle righe duplicate Esempio #16 - Trovare i record comuni tra le tabelle Esempio #17 - Raggruppare i dati con ROLLUP Esempio #18 - Somma condizionale Esempio #19 - Raggruppare le righe in base a un intervallo Esempio n. 20 - Calcolo di un totale progressivo in SQL Esempio n. 21 - Calcolo di una media mobile in SQL Esempio #22 - Calcolo della differenza (Delta) tra due colonne su righe diverse Esempio n. 23 - Calcolo della differenza tra un anno e l'altro Esempio n. 24 - Utilizzare Recursive Queries per gestire le gerarchie di dati Esempio #25 - Trovare la lunghezza di una serie utilizzando Window Functions Esercitarsi su Advanced SQL con i corsi di LearnSQL.com Uno dei modi migliori per imparare l'SQL avanzato è studiare query di esempio. In questo articolo mostreremo 25 esempi di query SQL avanzate di media e alta complessità. Potete usarli per rinfrescare le vostre conoscenze di SQL avanzato o per ripassare prima di un colloquio SQL. Molti degli esempi contenuti in questo articolo si basano sulla seguente tabella employee tabella. Solo alcuni esempi si baseranno su altre tabelle; in questi casi, le tabelle saranno spiegate insieme all'esempio. employee_idfirst_namelast_namedept_idmanager_idsalaryexpertise 100JohnWhiteIT103120000Senior 101MaryDannerAccount10980000junior 102AnnLynnSales107140000Semisenior 103PeterO'connorIT110130000Senior 106SueSanchezSales107110000Junior 107MartaDoeSales110180000Senior 109AnnDannerAccount11090000Senior 110SimonYangCEOnull250000Senior 111JuanGraueSales10237000Junior Anche per le persone con esperienza di SQL, un buon corso interattivo online di SQL può essere di grande aiuto. La serie più completa di corsi interattivi di SQL si trova nel nostro percorso SQL dalla A alla Z. Contiene 7 corsi interattivi di SQL con oltre 850 (!) esercizi disposti in modo logico per condurvi da un principiante assoluto a un utente avanzato di SQL. I corsi per principianti coprono le basi di SQL e sono un modo perfetto per rivedere e rinfrescare le conoscenze di base di SQL. I corsi SQL avanzati vi insegneranno concetti come le funzioni finestra, le query ricorsive e i report SQL complessi. Create un account gratuito su LearnSQL.it e provate i nostri corsi interattivi senza dover spendere nulla. Poi, se vi piace quello che state imparando, potete acquistare l'accesso completo. Ok, iniziamo con le query SQL avanzate! 25 esempi di query Advanced SQL con spiegazioni Esempio n. 1 - Classificare le righe in base a uno specifico criterio di ordinamento A volte è necessario creare una query SQL per mostrare una classifica di righe in base a un criterio d'ordine specifico. In questo esempio di query, mostreremo un elenco di tutti i dipendenti ordinati per stipendio (prima lo stipendio più alto). Il report includerà la posizione di ciascun dipendente nella classifica. Ecco il codice: SELECT employee_id, last_name, first_name, salary, RANK() OVER (ORDER BY salary DESC) as ranking FROM employee ORDER BY ranking Nella query precedente, utilizziamo la funzione RANK(). Si tratta di una funzione a finestra che restituisce la posizione di ciascuna riga nell'insieme dei risultati, in base all'ordine definito nella clausola OVER (1 per lo stipendio più alto, 2 per il secondo più alto e così via). È necessario utilizzare una clausola di ranking ORDER BY alla fine della query per indicare l'ordine in cui verrà mostrato l'insieme dei risultati. Se volete saperne di più sulle funzioni di ranking in SQL, vi consiglio il nostro articolo Cos'è la funzione RANK() in SQL e come si usa? Esempio n. 2 - Elencare le prime 5 righe di un insieme di risultati La prossima query SQL crea un report con i dati dei dipendenti per i primi 5 stipendi dell'azienda. Questo tipo di report deve essere ordinato in base a un determinato criterio; nel nostro esempio, il criterio d'ordine sarà ancora una volta salary DESC: WITH employee_ranking AS ( SELECT employee_id, last_name, first_name, salary, RANK() OVER (ORDER BY salary DESC) as ranking FROM employee ) SELECT employee_id, last_name, first_name, salary FROM employee_ranking WHERE ranking <= 5 ORDER BY ranking La clausola WITH della query precedente crea una CTE chiamata employee_ranking, che è una sorta di tabella virtuale che viene consumata nella query principale. La sottoquery nella CTE utilizza la funzione RANK() per ottenere la posizione di ogni riga nella classifica. La clausola OVER (ORDER BY salary DESC) indica come deve essere calcolato il valore RANK(). La funzione RANK() per la riga con il salario più alto restituirà 1, e così via. Infine, nella WHERE della query principale chiediamo le righe con un valore di ranking minore o uguale a 5. In questo modo otteniamo solo le prime 5 posizioni della classifica. In questo modo si ottengono solo le prime 5 righe per valore di ranking. Anche in questo caso, utilizziamo una clausola ORDER BY per mostrare l'insieme dei risultati, ordinati per rango crescente. Esempio n. 3 - Elencare le ultime 5 righe di un insieme di risultati Questa query è simile alla query top 5, ma vogliamo le ultime 5 righe. È sufficiente cambiare il tipo di ordine, cioè usare ASC invece di DESC. Nella CTE, creeremo una colonna di classificazione basata su un ordine ascendente di stipendio (prima lo stipendio più basso): WITH employee_ranking AS ( SELECT employee_id, last_name, first_name, salary, RANK() OVER (ORDER BY salary ASC) as ranking FROM employee ) SELECT employee_id, last_name, first_name, salary FROM employee_ranking WHERE ranking <= 5 ORDER BY ranking Nella query principale, utilizziamo WHERE ranking <= 5 per filtrare le righe con i 5 stipendi più bassi. Successivamente, si utilizza ORDER BY ranking per ordinare le righe del report in base al valore della classifica. Esempio 4 - Elencare la seconda riga più alta di un set di risultati Supponiamo di voler ottenere i dati del dipendente con il secondo stipendio più alto dell'azienda. Possiamo applicare un approccio simile alla nostra query precedente: WITH employee_ranking AS ( SELECT employee_id, last_name, first_name, salary, RANK() OVER (ORDER BY salary DESC) as ranking FROM employee ) SELECT employee_id, last_name, first_name, salary FROM employee_ranking WHERE ranking = 2 La condizione WHERE ranking = 2 è utilizzata per filtrare le righe con il salario in posizione 2. Si noti che è possibile avere più di un dipendente in posizione 2 se hanno lo stesso stipendio. A questo punto, è importante capire il comportamento della funzione RANK() e delle altre funzioni disponibili, come ROW_NUMBER() e DENSE_RANK(). Questo argomento è trattato in dettaglio nella nostra panoramica sulle funzioni di classificazione in SQL. Vi consiglio vivamente di leggere questo articolo se avete bisogno di lavorare con diversi tipi di classifiche. Esempio #5 - Elencare il secondo stipendio più alto per reparto Aggiungiamo una variante alla query SQL precedente. Poiché ogni dipendente appartiene a un reparto, ora vogliamo un report che mostri l'ID del reparto e il nome del dipendente con il secondo stipendio più alto in questo reparto. Vogliamo un record per ogni reparto dell'azienda. Ecco la query: WITH employee_ranking AS ( SELECT employee_id, last_name, first_name, salary, dept_id RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) as ranking FROM employee ) SELECT dept_id, employee_id, last_name, first_name, salary FROM employee_ranking WHERE ranking = 2 ORDER BY dept_id, last_name La principale modifica introdotta in questa query è la clausola PARTITION BY dept_id in OVER. Questa clausola raggruppa le righe che hanno lo stesso dept_id nome, ordinando le righe di ciascun gruppo in base allo stipendio DESC. Poi viene calcolata la funzione RANK() per ogni reparto. Nella query principale, vengono restituiti i dati dept_id e i dati dei dipendenti che si trovano nella posizione 2 della loro classifica di reparto. Per i lettori che vogliono saperne di più su come trovare l'ennesimariga più alta di un gruppo, consiglio di leggere l'articolo Come trovare l'ennesimo stipendio più alto per reparto con SQL. Esempio #6 - Elencare il primo 50% di righe in un insieme di risultati In alcuni casi, potremmo essere interessati a ottenere il primo 50% dell'insieme di risultati (o qualsiasi altra percentuale). Per questo tipo di report, esiste una funzione SQL chiamata NTILE() che riceve un parametro intero che indica il numero di sottoinsiemi in cui si desidera dividere l'intero set di risultati. Ad esempio NTILE(2) divide l'insieme dei risultati in 2 sottoinsiemi con la stessa quantità di elementi; per ogni riga restituisce un 1 o un 2 a seconda del sottoinsieme in cui si trova la riga. Ecco la query: WITH employee_ranking AS ( SELECT employee_id, last_name, first_name, salary, NTILE(2) OVER (ORDER BY salary ) as ntile FROM employee ) SELECT employee_id, last_name, first_name, salary FROM employee_ranking WHERE ntile = 1 ORDER BY salary La query precedente restituisce solo le righe della prima metà di un report di dipendenti ordinati per stipendio in ordine crescente. Utilizziamo la condizione ntile = 1 per filtrare solo le righe nella prima metà del report. Se siete interessati alla funzione finestra NTILE(), consultate l'articolo Common SQL Window Functions: Using Partitions With Ranking Functions. Esempio #7 - Elencare l'ultimo 25% di righe in un insieme di risultati Come per la query precedente, in questo esempio utilizzeremo NTILE(4) per dividere l'insieme dei risultati in 4 sottoinsiemi; ogni sottoinsieme avrà il 25% dell'insieme totale dei risultati. Utilizzando la funzione NTILE(), genereremo una colonna chiamata ntile con i valori 1, 2, 3 e 4: WITH employee_ranking AS ( SELECT employee_id, last_name, first_name, salary, NTILE(4) OVER (ORDER BY salary) as ntile FROM employee ) SELECT employee_id, last_name, first_name, salary FROM employee_ranking WHERE ntile = 4 ORDER BY salary La condizione WHERE ntile = 4 filtra solo le righe dell'ultimo trimestre del report. L'ultima clausola ORDER BY salary ordina l'insieme dei risultati restituiti dalla query, mentre OVER (ORDER BY salary) ordina le righe prima di dividerle in 4 sottoinsiemi utilizzando NTILE(4). Esempio #8 - Numerare le righe in un insieme di risultati A volte si vuole creare una classifica che assegni a ogni riga un numero che ne indichi la posizione nella classifica: 1 alla prima riga, 2 alla seconda e così via. SQL offre alcuni modi per farlo. Se vogliamo una semplice sequenza di numeri da 1 a N, possiamo usare la funzione ROW_NUMBER(). Se invece vogliamo una classifica che permetta di avere due righe nella stessa posizione (cioè perché condividono lo stesso valore), possiamo usare la funzione RANK() o DENSE_RANK(). La seguente query crea un report in cui ogni riga ha un valore di posizione: SELECT employee_id, last_name, first_name, salary, ROW_NUMBER() OVER (ORDER BY employee_id) as ranking_position FROM employee Se volete conoscere le diverse funzioni di classificazione avanzate, vi consiglio di leggere l'articolo Panoramica delle funzioni di classificazione in SQL. Esempio n. 9 - Elenco di tutte le combinazioni di righe di due tabelle In alcuni casi, potrebbe essere necessario un join che includa tutte le possibili combinazioni di righe di due tabelle. Supponiamo di avere un'azienda alimentare che vende 3 tipi di cereali: corn flakes, corn flakes zuccherati e fiocchi di riso. Tutti questi cereali sono venduti in 3 diverse confezioni: 1 libbra, 3 libbre e 5 libbre. Poiché offriamo 3 prodotti in 3 diverse dimensioni di confezione, offriamo nove diverse combinazioni. Abbiamo una product tabella con 3 record (corn flakes, corn flakes zuccherati e fiocchi di riso) e un'altra tabella chiamata box_size con 3 record, uno per 1 libbra e due record per 3 e 5 libbre, rispettivamente. Se vogliamo creare un report con il listino prezzi delle nostre nove combinazioni, possiamo utilizzare la seguente query: SELECT grain.product_name, box_size.description, grain.price_per_pound * box_size.box_weight FROM product CROSS JOIN box_sizes Il risultato della query sarà: productpackage_sizeprice Corn flake1 pound box2.43 Corn flake3 pound box7.29 Corn flake5 pound box12.15 Sugared corn flake1 pound box2.85 Sugared corn flake3 pound box8.55 Sugared corn flake5 pound box14.25 Rice flake1 pound box1.98 Rice flake3 pound box5.94 Rice flake5 pound box9.90 La clausola CROSS JOIN senza alcuna condizione produce una tabella con tutte le combinazioni di righe di entrambe le tabelle. Si noti che il prezzo viene calcolato in base al prezzo per libbra memorizzato nella tabella product e il peso da box_sizes con l'espressione: grain.price_per_pound * box_size.box_weight Un approfondimento su CROSS JOIN è disponibile in Guida illustrata alla funzione CROSS JOIN di SQL. Esempio n. 10 - Unire una tabella a se stessa In alcuni casi è necessario unire una tabella a se stessa. Pensate alla employee tabella. Ogni riga ha una colonna chiamata manager_id con l'ID del manager che supervisiona il dipendente. Utilizzando un self-join possiamo ottenere un report con le colonne employee_name e manager_name; questo ci mostrerà chi gestisce ogni dipendente. Ecco la query: SELECT e1.first_name ||’ ‘|| e1.last_name AS manager_name, e2.first_name ||’ ‘|| e2.last_name AS employee_name FROM employee e1 JOIN employee e2 ON e1.employee_id = e2.manager_id Nella query precedente, si può notare che la tabella employee viene citata due volte come e1 e e2, e la condizione di join è e1.employee_id = e2.manager_id. Questa condizione collega ogni riga di dipendente con la riga del manager. L'articolo Che cos'è una Self Join in SQL? Una spiegazione con sette esempi vi darà altre idee su quando è possibile applicare le auto-unioni nelle query SQL. Esempio #11 - Mostrare tutte le righe con un valore superiore alla media Abbiamo bisogno di un report che mostri tutti i dipendenti con uno stipendio superiore alla media aziendale. Possiamo prima creare una sottoquery per ottenere il salario medio dell'azienda e poi confrontare il salario di ogni dipendente con il risultato della sottoquery. Questo è mostrato nell'esempio seguente: SELECT first_name, last_name, salary FROM employee WHERE salary > ( SELECT AVG(salary) FROM employee ) È possibile vedere la sottoquery che ottiene il salario medio nella clausola WHERE. Nella query principale, vengono selezionati il nome del dipendente e lo stipendio. Per ulteriori informazioni sulle subquery, consultare l'articolo Come esercitarsi con le subquery in SQL. Esempio n. 12 - Dipendenti con stipendio superiore alla media del loro dipartimento Supponiamo di voler ottenere i record dei dipendenti con stipendio superiore alla media dei loro reparti. Questa query è diversa dalla precedente, perché ora abbiamo bisogno di una sottoquery per ottenere lo stipendio medio del reparto del dipendente corrente, anziché dell'intera azienda. Si chiama sottoquery correlata perché all'interno della sottoquery c'è un riferimento a una colonna della riga corrente della tabella principale della query. Ecco il codice: SELECT first_name, last_name, salary FROM employee e1 WHERE salary > (SELECT AVG(salary) FROM employee e2 WHERE e1.departmet_id = e2.department_id) Nella subquery, possiamo vedere un riferimento alla colonna e1.department_id, che è una colonna a cui si fa riferimento nella query principale. La condizione e1.departmet_id = e2.department_id è la chiave della subquery perché ci consente di ottenere la media di tutti i dipendenti del reparto della riga corrente. Una volta ottenuta la retribuzione media del reparto, la confrontiamo con la retribuzione del dipendente e filtriamo di conseguenza. Esempio n. 13 - Ottenere tutte le righe in cui è presente un valore nel risultato di una sottoquery Supponiamo che John Smith gestisca diversi reparti e che si voglia ottenere un elenco di tutti i dipendenti di tali reparti. Utilizzeremo una sottoquery per ottenere gli ID dei reparti gestiti da John Smith. Poi useremo l'operatore IN per trovare i dipendenti che lavorano in quei reparti: SELECT first_name, last_name FROM employee e1 WHERE department_id IN ( SELECT department_id FROM department WHERE manager_name=‘John Smith’) La subquery precedente è una subquery a più righe: restituisce più di una riga. Infatti, restituirà più righe perché John Smith gestisce molti reparti. Quando si lavora con subquery a più righe, è necessario utilizzare operatori specifici (come IN) nella condizione WHERE che coinvolge la subquery. Esempio #14 - Trovare righe duplicate in SQL Se una tabella ha righe duplicate, è possibile trovarle con l'SQL. Utilizzare una query con una clausola GROUP BY che includa tutte le colonne della tabella e una clausola HAVING per filtrare le righe che compaiono più di una volta. Ecco un esempio: SELECT employee_id, last_name, first_name, dept_id, manager_id, salary FROM employee GROUP BY employee_id, last_name, first_name, dept_id, manager_id, salary HAVING COUNT(*) > 1 Le righe che non sono duplicate avranno un COUNT(*) uguale a 1, ma le righe che esistono più volte avranno un COUNT(*) che restituisce il numero di volte che la riga esiste. Suggerisco l'articolo Come trovare i valori duplicati in SQL se volete trovare maggiori dettagli su questa tecnica. Esempio #15 - Conteggio delle righe duplicate Se si desidera contare le righe duplicate, è possibile utilizzare la seguente query. È simile alla precedente, ma aggiungiamo un COUNT(*) nell'elenco SELECT per mostrare quante volte ogni riga duplicata appare nella tabella: SELECT employee_id, last_name, first_name, dept_id, manager_id, salary, COUNT(*) AS number_of_rows FROM employee GROUP BY employee_id, last_name, first_name, dept_id, manager_id, salary HAVING COUNT(*) > 1 Anche in questo caso, potete trovare informazioni preziose su come gestire i record duplicati nell'articolo Come trovare i record duplicati in SQL. Esempio #16 - Trovare i record comuni tra le tabelle Se avete due tabelle con lo stesso schema o se due tabelle hanno un sottoinsieme di colonne in comune, potete ottenere le righe che compaiono in entrambe le tabelle con l'operatore set INTERSECT. Supponiamo di avere un'istantanea della tabella employee scattata nel gennaio 2020, chiamata employee_2020_jan e vogliamo ottenere l'elenco dei dipendenti presenti in entrambe le tabelle. Possiamo farlo con questa query: SELECT last_name, first_name FROM employee INTERSECT SELECT last_name, first_name FROM employee_2020_jan Come risultato, otterremo un elenco di dipendenti presenti in entrambe le tabelle. Forse avranno valori diversi nelle colonne salary o dept_id. In altre parole, otterremo i dipendenti che lavoravano per l'azienda nel gennaio 2020 e che stanno ancora lavorando per l'azienda. Se siete interessati a saperne di più sugli operatori di set, vi suggerisco l'articolo Introduzione agli operatori di set di SQL: Union, Union All, Minus e Intersect. Esempio #17 - Raggruppare i dati con ROLLUP La clausola GROUP BY in SQL viene utilizzata per aggregare le righe in gruppi e applicare funzioni a tutte le righe del gruppo, restituendo un unico valore di risultato. Ad esempio, se si desidera ottenere un report con il totale degli stipendi per reparto e livello di competenza, è possibile eseguire la seguente query: SELECT dept_id, expertise, SUM(salary) total_salary FROM employee GROUP BY dept_id, expertise GROUP BY ha la clausola opzionale ROLLUP, che consente di includere raggruppamenti aggiuntivi in una query. Aggiungendo la clausola ROLLUP al nostro esempio, potremmo ottenere la somma totale degli stipendi per ogni reparto (indipendentemente dal livello di competenza del dipendente) e la somma totale degli stipendi per l'intera tabella (indipendentemente dal reparto e dal livello di competenza del dipendente). La query modificata è: SELECT dept_id, expertise, SUM(salary) total_salary FROM employee GROUP BY ROLLUP (dept_id, expertise) E il risultato sarà: dept_idexpertisetotal_salary AccountSenior90000 AccountJunior80000 AccountNULL170000 CEOSenior250000 CEONULL250000 ITSenior250000 ITNULL250000 SalesJunior110000 SalesSemisenior140000 SalesSenior180000 SalesNULL430000 NULLNULL1100000 Le righe nel set di risultati con NULL sono le righe aggiuntive aggiunte dalla clausola ROLLUP. Un valore NULL nella colonna expertise indica un gruppo di righe per un valore specifico di dept_id, ma senza un valore specifico expertise. In altre parole, è l'importo totale degli stipendi per ogni dept_id. Allo stesso modo, l'ultima riga del risultato con NULL per le colonne dept_id e expertise indica il totale generale per tutti i reparti dell'azienda. Per saperne di più sulla clausola ROLLUP e su altre clausole simili come CUBE, l'articolo Raggruppare, arrotolare e cubare i dati contiene molti esempi. Esempio #18 - Somma condizionale In alcuni casi, è necessario riassumere o contare i valori in base ad alcune condizioni. Ad esempio, se si vuole ottenere il totale degli stipendi dei reparti Vendite e Risorse umane combinati e dei reparti IT e Assistenza combinati, si può eseguire la seguente query: SELECT SUM (CASE WHEN dept_id IN (‘SALES’,’HUMAN RESOURCES’) THEN salary ELSE 0 END) AS total_salary_sales_and_hr, SUM (CASE WHEN dept_id IN (‘IT’,’SUPPORT’) THEN salary ELSE 0 END) AS total_salary_it_and_support FROM employee La query restituisce una singola riga con due colonne. La prima colonna mostra il totale degli stipendi dei reparti Vendite e Risorse umane. Questo valore viene calcolato utilizzando la funzione SUM() sulla colonna salary, ma solo quando il dipendente appartiene al reparto Vendite o Risorse umane. Quando il dipendente appartiene a qualsiasi altro reparto, alla somma viene aggiunto uno zero. La stessa idea viene applicata alla colonna total_salary_it_and_support. Gli articoli Modelli utili di SQL: Riassunto condizionale con CASE e Come utilizzare CASE WHEN con SUM() in SQL forniscono maggiori dettagli su questa tecnica. Esempio #19 - Raggruppare le righe in base a un intervallo Nel prossimo esempio di query, creeremo gli intervalli salariali low, medium e high. Poi conteremo quanti dipendenti si trovano in ogni fascia salariale: SELECT CASE WHEN salary <= 750000 THEN ‘low’ WHEN salary > 750000 AND salary <= 100000 THEN ‘medium’ WHEN salary > 100000 THEN ‘high’ END AS salary_category, COUNT(*) AS number_of_employees FROM employee GROUP BY CASE WHEN salary <= 750000 THEN ‘low’ WHEN salary > 750000 AND salary <= 100000 THEN ‘medium’ WHEN salary > 100000 THEN ‘high’ END In questa query, utilizziamo CASE per definire l'intervallo di stipendio di ciascun dipendente. Si può vedere la stessa istruzione CASE due volte. La prima definisce gli intervalli, come abbiamo appena detto; la seconda nel GROUP BY aggrega i record e applica la funzione COUNT(*) a ogni gruppo di record. È possibile utilizzare l'istruzione CASE nello stesso modo per calcolare conteggi o somme per altri livelli definiti in modo personalizzato. Come usare CASE in SQL spiega altri esempi di istruzioni CASE come quella usata in questa query. Esempio n. 20 - Calcolo di un totale progressivo in SQL Il totale progressivo è un modello SQL molto comune, usato spesso in finanza e nell'analisi delle tendenze. Quando si dispone di una tabella che memorizza una qualsiasi metrica giornaliera, come ad esempio una tabella sales con le colonne day e daily_amount, è possibile calcolare il totale corrente come somma cumulativa di tutti i valori daily_amount precedenti. SQL mette a disposizione una funzione chiamata SUM() per fare questo. Nella seguente query, calcoleremo le vendite cumulative per ogni giorno: SELECT day, daily_amount, SUM (daily_amount) OVER (ORDER BY day) AS running_total FROM sales La funzione SUM() utilizza la clausola OVER() per definire l'ordine delle righe; tutte le righe precedenti al giorno corrente sono incluse nella SUM(). Ecco un risultato parziale: daydaily_amountrunning_total Jan 30, 20231000.001000.00 Jan 31, 2023800.001800.00 Feb 1, 2023700.002500.00 Le prime due colonne day e daily_amount sono valori presi direttamente dalla tabella sales. La colonna running_total è calcolata dall'espressione: SUM (daily_amount) OVER (order by day) Si può vedere chiaramente come running_total sia la somma cumulativa dei precedenti daily_amounts. Se volete approfondire questo argomento, vi suggerisco l'articolo Che cos'è un totale corrente in SQL e come si calcola?, che contiene molti esempi chiarificatori. Esempio n. 21 - Calcolo di una media mobile in SQL La media mobile è una tecnica di analisi delle serie temporali per analizzare le tendenze dei dati. Viene calcolata come media del valore corrente e di un numero specifico di valori immediatamente precedenti per ogni punto nel tempo. L'idea principale è quella di esaminare il comportamento di queste medie nel tempo, invece di esaminare il comportamento dei punti di dati originali o grezzi. Calcoliamo la media mobile degli ultimi 7 giorni utilizzando la tabella dell'esempio precedente sales dell'esempio precedente: SELECT day, daily_amount, AVG (daily_amount) OVER (ORDER BY day ROWS 6 PRECEDING) AS moving_average FROM sales Nella query precedente, utilizziamo la funzione AVG() window per calcolare la media utilizzando la riga corrente (oggi) e le 6 righe precedenti. Poiché le righe sono ordinate per giorno, la riga corrente e le 6 righe precedenti definiscono un periodo di 1 settimana. L'articolo Cos'è una media mobile e come calcolarla in SQL approfondisce questo argomento; consultatelo se volete saperne di più. Esempio #22 - Calcolo della differenza (Delta) tra due colonne su righe diverse Esiste più di un modo per calcolare la differenza tra due righe in SQL. Un modo per farlo è utilizzare le funzioni di finestra LEAD() e LAG(), come faremo in questo esempio. Supponiamo di voler ottenere un report con il totale delle vendite di ogni giorno, ma di voler ottenere anche la differenza (o delta) rispetto al giorno precedente. Possiamo utilizzare una query come questa: SELECT day, daily_amount, daily_amount - LAG(daily_amount) OVER (ORDER BY day) AS delta_yesterday_today FROM sales L'espressione chiave di questa query è: daily_amount - LAG(daily_amount) OVER (ORDER BY day) Entrambi gli elementi della differenza aritmetica provengono da righe diverse. Il primo elemento proviene dalla riga corrente e LAG(daily_amount) proviene dalla riga del giorno precedente. LAG() restituisce il valore di qualsiasi colonna della riga precedente (in base a ORDER BY specificato nella clausola OVER ). Se volete saperne di più su LAG() e LEAD(), vi suggerisco l'articolo Come calcolare la differenza tra due righe in SQL. Esempio n. 23 - Calcolo della differenza tra un anno e l'altro I confronti anno su anno (YOY) o mese su mese sono un modo popolare ed efficace per valutare le prestazioni di diversi tipi di organizzazioni. È possibile calcolare il confronto come valore o come percentuale. In questo esempio, utilizzeremo la tabella sales che contiene dati con granularità giornaliera. Per prima cosa dobbiamo aggregare i dati all'anno o al mese, cosa che faremo creando una CTE con gli importi aggregati per anno. Ecco la query: WITH year_metrics AS ( SELECT extract(year from day) as year, SUM(daily_amount) as year_amount FROM sales GROUP BY year) SELECT year, year_amount, LAG(year_amount) OVER (ORDER BY year) AS revenue_previous_year, year_amount - LAG(year_amount) OVER (ORDER BY year) as yoy_diff_value, ((year_amount - LAG(year_amount) OVER (ORDER BY year) ) / LAG(year_amount) OVER (ORDER BY year)) as yoy_diff_perc FROM year_metrics ORDER BY 1 La prima espressione da analizzare è quella utilizzata per calcolare yoy_diff_value: year_amount - LAG(year_amount ) OVER (ORDER BY year) Viene utilizzata per calcolare la differenza (come valore) tra l'importo dell'anno corrente e quello dell'anno precedente, utilizzando la funzione LAG() e ordinando i dati per anno. Nell'espressione successiva, si calcola la stessa differenza in percentuale. Questo calcolo è un po' più complesso perché è necessario dividere per l'importo dell'anno precedente. (Nota: l'anno precedente viene utilizzato come base per il calcolo della percentuale, quindi l'anno precedente corrisponde al 100%). ((year_amount-LAG(year_amount ) OVER(ORDER BY year))/LAG(year_amount ) OVER(ORDER BY year)) Nell'articolo Come calcolare le differenze anno su anno in SQL sono riportati diversi esempi di calcolo delle differenze anno su anno e mese su mese. Esempio n. 24 - Utilizzare Recursive Queries per gestire le gerarchie di dati Alcune tabelle in SQL possono avere una gerarchia di dati implicita. Ad esempio, la nostra tabella employee ha una tabella manager_id per ogni dipendente. Abbiamo un manager che è responsabile di altri manager, che a loro volta hanno altri dipendenti sotto la loro responsabilità, e così via. Quando abbiamo questo tipo di organizzazione, possiamo avere una gerarchia di vari livelli. In ogni riga, la colonna manager_id si riferisce alla riga del livello immediatamente superiore della gerarchia. In questi casi, una richiesta frequente è quella di ottenere un elenco di tutti i dipendenti che fanno capo (direttamente o indirettamente) all'amministratore delegato dell'azienda (che, in questo caso, ha la employee_id di 110). La query da utilizzare è: WITH RECURSIVE subordinate AS ( SELECT employee_id, first_name, last_name, manager_id FROM employee WHERE employee_id = 110 -- id of the top hierarchy employee (CEO) UNION ALL SELECT e.employee_id, e.first_name, e.last_name, e.manager_id FROM employee e JOIN subordinate s ON e.manager_id = s.employee_id ) SELECT employee_id, first_name, last_name, manager_id FROM subordinate ; In questa query, abbiamo creato una CTE ricorsiva chiamata subordinate. È la parte fondamentale di questa query perché attraversa la gerarchia dei dati passando da una riga alle righe della gerarchia immediatamente inferiore. Ci sono due sottoquery collegate da un UNION ALL; la prima sottoquery restituisce la riga superiore della gerarchia e la seconda query restituisce il livello successivo, aggiungendo tali righe al risultato intermedio della query. Quindi la seconda subquery viene eseguita di nuovo per restituire il livello successivo, che verrà nuovamente aggiunto al risultato intermedio della query. Questo processo viene ripetuto finché non vengono aggiunte nuove righe al risultato intermedio. Infine, la query principale consuma i dati della CTE subordinate e restituisce i dati nel modo previsto. Se volete saperne di più sulle query ricorsive in SQL, vi suggerisco l'articolo Come trovare tutti i dipendenti sotto ogni manager in SQL. Esempio #25 - Trovare la lunghezza di una serie utilizzando Window Functions Supponiamo di avere una tabella con i dati di registrazione degli utenti. Vengono memorizzate le informazioni sul numero di utenti registrati in ogni data. Definiamo una serie di dati come la sequenza di giorni consecutivi in cui gli utenti si sono registrati. Un giorno in cui nessun utente si registra interrompe la serie di dati. Per ogni serie di dati, vogliamo trovare la sua lunghezza. La tabella seguente mostra le serie di dati: iddayRegistered users 1Jan 25 202351 2Jan 26 202346 3Jan 27 202341 4Jan 30 202359 5Jan 31 202373 6Feb 1 202334 7Feb 2 202356 8Feb 4 202334 Ci sono 3 diverse serie di dati mostrate con colori diversi. Stiamo cercando una query per ottenere la lunghezza di ciascuna serie di dati. La prima serie di dati inizia il 25 gennaio e ha una lunghezza di 3 elementi, la seconda inizia il 30 gennaio e la sua lunghezza è 4, e così via. La query è la seguente: WITH data_series AS ( SELECT RANK() OVER (ORDER BY day) AS row_number, day, day - RANK() OVER (ORDER BY day) AS series_id FROM user_registration ) SELECT MIN(day) AS series_start_day, MAX(day) AS series_end_day, MAX(day) - MIN (day) + 1 AS series_length FROM data_series GROUP BY series_id ORDER BY series_start_date Nella query precedente, la CTE ha la colonna series_id, che è un valore destinato a essere utilizzato come ID per le righe della stessa serie di dati. Nella query principale, la clausola GROUP BY series_id viene utilizzata per aggregare le righe della stessa serie di dati. Quindi si può ottenere l'inizio della serie con MIN(day) e la sua fine con MAX(day). La lunghezza della serie viene calcolata con l'espressione: MAX(day) - MIN (day) + 1 Se volete approfondire questo argomento, l'articolo Come calcolare la lunghezza di una serie con SQL fornisce una spiegazione dettagliata di questa tecnica. Esercitarsi su Advanced SQL con i corsi di LearnSQL.com SQL è un linguaggio facile da imparare e potente. In questo articolo abbiamo mostrato 25 esempi di query SQL avanzate. Tutti possono essere spiegati in circa 5 minuti, a dimostrazione del fatto che SQL è un linguaggio accessibile anche quando si devono realizzare report o query complesse. Se volete continuare a imparare l'SQL, vi suggerisco i nostri corsi di SQL avanzato: Window Functions (Funzioni Finestra), Query ricorsive, e Istruzione GROUP BY in SQL ed estensioni. Tutti coprono aree complesse del linguaggio SQL con parole semplici e numerosi esempi. Aumentate le vostre competenze e investite in voi stessi con SQL! Tags: sql imparare sql esercizi online advanced sql