20th Jul 2022 Tempo di lettura: 14 minuti 6 query SQL avanzate per l'analisi dei dati finanziari Tihomir Babic sql esercizi online analisi dati Indice Introduzione al database 6 esempi di query Advanced SQL per l'analisi finanziaria 1: Raggruppare i dati per anno e trimestre 2: Calcolo dei totali progressivi 3: Calcolo delle medie consecutive 4: Analisi delle serie temporali 5: Aggiungere più livelli di raggruppamento 6: Creare un rapporto sulle entrate a livello annuale Vi piace usare le query di Advanced SQL nell'analisi dei dati finanziari? Siete utenti avanzati di SQL e siete alle prime armi con la finanza? Volete imparare a utilizzare le vostre conoscenze per analizzare i dati finanziari? L'articolo vi fornirà alcune indicazioni. Probabilmente sapete che SQL è uno strumento potente per analizzare qualsiasi tipo di dati. Più i dati sono grandi e complessi, più SQL diventa utile. In alcune situazioni, i dati finanziari possono essere molto complicati e la produzione di un'analisi sofisticata richiede strumenti sofisticati. SQL è uno di questi strumenti e più si conoscono alcune pratiche avanzate di SQL, più diventa facile eseguire analisi complesse di dati finanziari e creare report finanziari. Vi mostrerò alcuni esempi di come farlo, tratti dalla mia esperienza personale. Il livello di conoscenza dell'SQL richiesto in questo articolo - come le funzioni di finestra, le estensioni GROUP BY e le query ricorsive - è trattato nella traccia LearnSQL.it Advanced SQL . Introduzione al database Tutti e sei gli esempi utilizzeranno questo database, composto da cinque tabelle: country card_type customer card_number card_transaction Immaginiamo che questo sia il database di una società di elaborazione di carte di credito e che voi siate il loro dipendente di riferimento per l'analisi dei dati finanziari. Entriamo ora nei dettagli di ciascuna tabella. La tabella country ha i seguenti attributi: id - L'ID del Paese e la chiave primaria (PK) di questa tabella. country_name - Il nome del Paese. La tabella successiva è card_typecon questi attributi: id - L'ID del tipo di carta; la chiave primaria (PK). card_type_name - Il nome del tipo di carta. La tabella customer è composta dalle colonne: id - L'ID del cliente; la chiave primaria (PK). NIN - Il numero di identificazione nazionale del cliente. first_name - Il nome del cliente. last_name - Il cognome del cliente. country_id - L'ID del Paese; si tratta di una chiave esterna (FK) che fa riferimento alla tabella country. Segue la tabella card_number con gli attributi: id - L'ID della carta; la chiave primaria (PK). card_number - Il numero della carta. customer_id - L'ID del cliente; una chiave esterna (FK) che fa riferimento alla tabella customer. card_type_id - L'ID del tipo di carta; una chiave esterna (FK) che fa riferimento alla tabella. card_type. L'ultima è la tabella card_transaction che contiene le colonne: id - L'ID della transazione; la chiave primaria (PK). date - La data della transazione. amount - L'importo della transazione, in dollari. card_number_id - ID della carta; una chiave esterna (FK) che fa riferimento alla tabella . card_number. Ora che avete familiarizzato con il database, passiamo agli esempi! 6 esempi di query Advanced SQL per l'analisi finanziaria 1: Raggruppare i dati per anno e trimestre Di solito i dati finanziari devono essere raggruppati in periodi o in fasce temporali specifiche. Se state creando dei report finanziari, avrete sicuramente bisogno di raggruppare i dati in anni e trimestri. Vi mostrerò come fare. L'apprendimento di questo principio vi consentirà di raggruppare i dati a qualsiasi altro livello (ad esempio mesi, settimane o giorni, a seconda dei dati in vostro possesso). In questo esempio, raggrupperò i dati per anni e trimestri e mostrerò il numero di transazioni. Per ottenere questo risultato, utilizzerò la tabella card_transaction e questo codice: SELECT EXTRACT(YEAR FROM date) AS year, EXTRACT(QUARTER FROM date) AS quarter, COUNT(amount) AS number_of_transactions FROM card_transaction GROUP BY EXTRACT(YEAR FROM date), EXTRACT(QUARTER FROM date) ORDER BY EXTRACT(YEAR FROM date) ASC, EXTRACT(QUARTER FROM date); Il codice utilizza la funzione EXTRACT() per ottenere gli anni e i trimestri. Si tratta di una funzione SQL standard, che funziona in MySQL e PostgreSQL. Tuttavia, se si utilizza SQL Server, è necessario utilizzare una funzione diversa, che verrà spiegata tra poco. Quando si utilizza la funzione EXTRACT(), è necessario specificare il periodo desiderato e la colonna che la funzione utilizzerà per restituire il periodo desiderato. Innanzitutto, voglio ottenere gli anni dalla colonna della data. Poi voglio i trimestri, sempre dalla colonna della data. Successivamente, devo contare il numero di transazioni, cosa che ho fatto utilizzando la funzione COUNT(). I dati vengono raggruppati dalle funzioni EXTRACT(), che rappresentano gli anni e i trimestri. Infine, i dati vengono ordinati per anni e trimestri in modo crescente, poiché voglio vedere il primo trimestre del primo anno in cima al rapporto. Sono sicuro che volete vedere il risultato: yearquarternumber_of_transactions 20191131 20192132 20193138 2019499 20201129 20202123 20203138 20204110 Questo report ha un aspetto molto bello, devo dire! Ora, se utilizzate SQL Server, dovrete utilizzare la funzione DATEPART() invece di EXTRACT(). Ecco la prima parte del codice, per essere sicuri di aver capito. Nel resto del codice si segue lo stesso principio: SELECT DATEPART(YEAR, date) AS year, DATEPART(QUARTER, date) AS quarter, COUNT(amount) AS number_of_transactions ... 2: Calcolo dei totali progressivi Il vostro compito ora è quello di mostrare i totali correnti per tutte le transazioni effettuate nel dicembre 2020. Il report deve essere aggregato a livello di tipo di carta. Per creare questo report, è necessario introdurre nel codice le funzioni delle finestre e JOINs. Prima di andare avanti, forse è il caso di verificare le proprie conoscenze di SQL. È possibile farlo nella traccia SQL Reporting. Questa traccia vi insegnerà anche a creare report SQL di base e a eseguire analisi sull'andamento dei ricavi e sul comportamento dei clienti. Il codice che restituirà i dati desiderati è il seguente: SELECT DISTINCT (ct.date), cty.card_type_name, SUM (ct.amount) OVER (PARTITION BY cty.card_type_name ORDER BY ct.date ASC) AS transaction_running_total FROM card_transaction ct JOIN card_number cn ON ct.card_number_id = cn.id JOIN card_type cty ON cn.card_type_id = cty.id WHERE date > '2020-11-30' AND date <= '2020-12-31' ORDER BY cty.card_type_name ASC; Il codice seleziona innanzitutto la data specifica, poiché possono esserci più transazioni giornaliere con lo stesso tipo di carta e persino con lo stesso numero di carta. Segue poi la colonna card_type_name. L'ultima colonna della query è transaction_running_total. Per calcolare il totale progressivo, ho utilizzato una funzione a finestra. Innanzitutto, i dati della colonna importo vengono sommati. Poi, utilizzando la clausola OVER(), ho specificato che il totale progressivo deve essere calcolato a livello di tipo di carta, quindi PARTITION BY cty.card_type_name. Infine, voglio che il totale progressivo sia calcolato dalla data più vecchia a quella più recente: ORDER BY date ASC. Per ottenere i dati, ho dovuto unire tre tabelle. La prima unione collega le tabelle card_transaction e card_number e le tabelle. La seconda unione fa riferimento alla tabella card_typeche è il modo in cui ottengo il nome del tipo di carta nel mio report. Ho assegnato degli alias a tutte le tabelle unite; in questo modo ho dovuto digitare di meno. Il risultato viene filtrato utilizzando la clausola WHERE, che mi permette di ottenere solo le transazioni di dicembre 2020. Ho deciso di ordinare i dati in base al nome del tipo di carta in ordine crescente. Eseguendo il codice si otterrà il report mostrato di seguito: datecard_type_nametransaction_running_total 2020-12-03diners-club-international8,988.79 2020-12-05diners-club-international23,403.95 2020-12-10diners-club-international38,396.95 2020-12-12diners-club-international51,525.07 2020-12-13diners-club-international61,643.00 2020-12-27diners-club-international89,522.36 2020-12-01maestro15,712.84 2020-12-03maestro31,737.02 2020-12-07maestro49,407.66 2020-12-08maestro60,526.36 2020-12-09maestro77,920.67 2020-12-12maestro92,465.81 2020-12-18maestro93,938.04 2020-12-19maestro110,541.99 2020-12-21maestro124,455.78 2020-12-23maestro127,626.83 2020-12-25maestro147,227.82 2020-12-26maestro170,589.49 2020-12-30maestro195,366.68 2020-12-01visa-electron16,881.70 2020-12-03visa-electron34,257.49 2020-12-13visa-electron51,982.98 2020-12-15visa-electron60,691.21 2020-12-22visa-electron80,816.65 2020-12-24visa-electron100,459.96 2020-12-29visa-electron104,595.89 2020-12-30visa-electron115,599.67 Se volete saperne di più sui totali cumulativi in SQL, leggete questo articolo per capire cosa sono i totali progressivi e come calcolarli. 3: Calcolo delle medie consecutive Il prossimo esempio prevede la creazione di un report che mostri tutte le transazioni del dicembre 2020 e il loro importo per le carte Visa Electron. Inoltre, verrà mostrato l'importo medio delle transazioni giornaliere utilizzando una media mobile. Date un'occhiata al codice: SELECT ct.date, cty.card_type_name, SUM(ct.amount) AS daily_sum, AVG(SUM(ct.amount)) OVER (ORDER BY ct.date ASC ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS transaction_running_average FROM card_transaction ct JOIN card_number cn ON ct.card_number_id = cn.id JOIN card_type cty ON cn.card_type_id = cty.id WHERE ct.date > '2020-11-30' AND date <= '2020-12-31' AND cty.card_type_name = 'visa-electron' GROUP BY ct.date, cty.card_type_name ORDER BY cty.card_type_name; Ho prima selezionato le colonne che mostreranno la data della transazione e il nome del tipo di carta. Poi ho sommato gli importi delle transazioni giornaliere e il risultato è stato mostrato nella colonna daily_sum. Questa operazione è stata necessaria perché è possibile che vengano effettuate diverse transazioni giornaliere con Visa Electron. Poi ho usato questa somma giornaliera dei valori delle transazioni per calcolare la sua media. Tuttavia, per ottenere la media mobile, devo utilizzare la clausola OVER(). Voglio che la media mobile sia calcolata dal primo all'ultimo giorno di dicembre 2020, quindi l'operazione è ordinata per data crescente. Per il calcolo delle medie mobili, ho deciso di utilizzare una media mobile a tre giorni, definita dalla seguente parte del codice: ROWS BETWEEN 2 PRECEDING AND CURRENT ROW. Questo indica al codice di utilizzare la riga corrente e le due righe precedenti (tre righe/tre date in totale) per calcolare la media mobile. Le tabelle vengono poi unite esattamente come nell'esempio precedente. Nella clausola WHERE sono presenti due condizioni: una definisce le date prese in considerazione, l'altra definisce il tipo di carta. Questa query darà come risultato la seguente tabella: datecard_type_namedaily_sumtransaction_running_average 2020-12-01visa-electron16,881.7016,881.70 2020-12-03visa-electron17,375.7917,128.75 2020-12-13visa-electron17,725.4917,327.66 2020-12-15visa-electron8,708.2314,603.17 2020-12-22visa-electron20,125.4415,519.72 2020-12-24visa-electron19,643.3116,158.99 2020-12-29visa-electron4,135.9314,634.89 2020-12-30visa-electron11,003.7811,594.34 Se non avete familiarità con le medie mobili, vi spiego come funzionano. Sapete già che si tratta di una media mobile a tre giorni, che utilizza tre righe per calcolare la media. Per la data '2020-12-01', la media nella tabella precedente è uguale alla somma giornaliera. Questo perché la query può utilizzare solo i dati della riga corrente, senza le righe precedenti. Per la data '2020-12-03', la media mobile viene calcolata come segue: (16,881.70 + 17,375.79) / 2 = 17,128.75. Attenzione! La media mobile non viene calcolata nel seguente modo: (16.881,70 + 17.375,79) / 3 = 11.419,16. Questo perché c'è solo una riga precedente, o solo due valori da calcolare. La media mobile per la data "2020-12-13" viene calcolata nel modo seguente: (16.881,70 + 17.375,79 + 17.725,49) / 3 = 17.327,66. 4: Analisi delle serie temporali Un'esigenza molto comune quando si lavora con i dati finanziari è quella di analizzare una serie temporale (cioè la differenza tra periodi di tempo, come giorno per giorno o mese per mese). Ad esempio, supponiamo che il vostro capo vi abbia chiesto di creare un report che mostri le variazioni giornaliere dei valori delle transazioni effettuate dai clienti cinesi nel mese di dicembre 2020. Il reparto vendite in Cina non è soddisfatto delle prestazioni di dicembre 2020, quindi vuole analizzare questo mese in dettaglio per scoprire dove si è verificato un calo delle transazioni. Per creare un report di questo tipo, è necessaria una funzione SQL window. Questa volta si tratta della funzione LAG(), che consente di recuperare i dati dalla riga precedente. Ecco come procedere: SELECT ct.date, SUM(ct.amount) AS daily_sum, (SUM(ct.amount)-LAG(SUM(ct.amount)) OVER (ORDER BY ct.date ASC)) AS daily_difference, co.country_name FROM card_transaction ct JOIN card_number cn ON ct.card_number_id = cn.id JOIN customer cu ON cn.customer_id = cu.id JOIN country co ON cu.country_id = co.id WHERE ct.date > '2020-11-30' AND date <= '2020-12-31' AND co.country_name = 'China' GROUP BY ct.date, co.country_name; La query inizia con un processo già noto: seleziona la data, quindi calcola la somma giornaliera delle transazioni (nel caso in cui ci siano diverse transazioni giornaliere dalla Cina). Per calcolare la differenza giornaliera, è necessario dedurre la somma delle transazioni del giorno precedente da quella del giorno corrente. Il calcolo viene effettuato da questa parte della query: (SUM(ct.amount)-LAG(SUM(ct.amount)) OVER (ORDER BY ct.date ASC)) AS daily_difference. La somma delle transazioni del giorno precedente viene restituita dalla funzione LAG() combinata con la clausola OVER(). L'operazione deve essere eseguita dalla data più vecchia a quella più recente, specificata in ORDER BY ct.date ASC. L'ultima colonna della query è la colonna country_name della tabella Paese. Per ottenere i dati richiesti vengono unite tre tabelle; ancora una volta ho usato degli alias. Nella clausola WHERE sono presenti due condizioni, una che definisce la data e l'altra che definisce il paese. Infine, il risultato è raggruppato per data e nome del paese. Eseguire la query per ottenere questa tabella: datedaily_sumdaily_differencecountry 2020-12-0116,881.70NULLChina 2020-12-0316,024.18-857.52China 2020-12-0717,670.641,646.46China 2020-12-082,856.29-14,814.35China 2020-12-0917,394.3114,538.02China 2020-12-1214,545.14-2,849.17China 2020-12-181,472.23-13,072.91China 2020-12-1910,821.769,349.53China 2020-12-2220,125.449,303.68China 2020-12-233,171.05-16,954.39China 2020-12-2419,643.3116,472.26China 2020-12-2519,600.99-42.32China 2020-12-2617,514.61-2,086.38China 2020-12-294,135.93-13,378.68China 2020-12-3026,393.1022,257.17China Il primo valore è NULL perché non ci sono righe precedenti alla prima, cioè la prima riga non ha una riga con cui essere confrontata. Per ulteriori informazioni sulla funzione LAG(), consultare questo articolo sul calcolo della differenza tra due righe. Se volete esercitarvi con le funzioni finestra, provate il nostro corso SQL Window Functions . 5: Aggiungere più livelli di raggruppamento Il report che vi ho mostrato nel primo esempio è abbastanza buono, ma questo non significa che non possa essere migliore. Manca, ad esempio, un subtotale del conteggio per il 2019 e il 2020 e un totale generale, come le tabelle pivot di Excel. Questo report può essere facilmente migliorato utilizzando la funzione ROLLUP(). Aggiungiamo alcuni subtotali e mostriamo tutti i valori anche a livello di tipo di tessera. Ecco come fare: SELECT EXTRACT(YEAR FROM ct.date) AS year, EXTRACT(QUARTER FROM ct.date) AS quarter, COUNT(ct.amount) AS number_of_transactions, cty.card_type_name FROM card_transaction ct JOIN card_number cn ON ct.card_number_id = cn.id JOIN card_type cty ON cn.card_type_id = cty.id GROUP BY ROLLUP(EXTRACT(YEAR FROM ct.date), EXTRACT(QUARTER FROM ct.date), cty.card_type_name); La prima parte del codice è tratta direttamente dal primo esempio. Utilizza la funzione EXTRACT() per classificare le date in anni e trimestri; quindi conta il numero di transazioni utilizzando la funzione COUNT(). L'ultima colonna selezionata dal codice è card_type_name dalla tabella card_type. I dati vengono selezionati dalle tabelle card_transaction, card_number, e card_type, che vengono unite. Ora viene la parte magica: l'uso di ROLLUP(), che è un'estensione della clausola GROUP BY. Dopo aver scritto GROUP BY nel codice, è sufficiente utilizzare la funzione ROLLUP() per specificare più livelli di raggruppamento nel report. Dato che il vostro compito è quello di raggruppare i dati a livello annuale, trimestrale e di tipo di carta, queste colonne dovrebbero essere contenute in ROLLUP(). Eseguite il codice sopra riportato e otterrete questo bel report: yearquarternumber_of_transactionscard_type_name 2019149diners-club-international 2019146maestro 2019136visa-electron 20191131NULL 2019244diners-club-international 2019244maestro 2019244visa-electron 20192132NULL 2019344diners-club-international 2019356maestro 2019338visa-electron 20193138NULL 2019423diners-club-international 2019442maestro 2019434visa-electron 2019499NULL 2019NULL500NULL 2020139diners-club-international 2020159maestro 2020131visa-electron 20201129NULL 2020233diners-club-international 2020250maestro 2020240visa-electron 20202123NULL 2020341diners-club-international 2020357maestro 2020340visa-electron 20203138NULL 2020426diners-club-international 2020448maestro 2020436visa-electron 20204110NULL 2020NULL500NULL NULLNULL1000NULL Non lasciatevi spaventare dai valori NULL! È tutto a posto nel report; i valori NULL appaiono quando c'è un subtotale, un totale o un totale generale. Ecco la prima parte della tabella: yearquarternumber_of_transactionscard_type_name 2019149diners-club-international 2019146maestro 2019136visa-electron 20191131NULL Questa tabella mostra il numero di transazioni del primo trimestre del 2019 per tipo di carta. Il totale parziale di tutte le transazioni del primo trimestre è 49 + 46 +36 = 131. La logica è la stessa per il resto della tabella. Quindi, ad esempio, quando si vede una riga come quella sottostante, significa che il numero totale di transazioni per l'anno 2019 è 500: yearquarternumber_of_transactionscard_type_name 2019NULL500NULL ROLLUP() e le altre estensioni di GROUP BY sono interessanti; mi piacciono molto! Se volete vedere quanto possono essere utili, il nostro corso sulle estensioni GROUP BY fa al caso vostro. Passiamo ora all'ultimo esempio di query SQL per l'analisi dei dati finanziari. 6: Creare un rapporto sulle entrate a livello annuale Questa sarà probabilmente la query più complessa che vi mostrerò, ma credo che ne valga la pena. Imparare cosa fa questa query vi consentirà di creare rapporti complessi e di eseguire analisi statistiche, due operazioni molto spesso richieste nel settore finanziario. Il vostro compito è creare un report che mostri le entrate del 2020, le entrate del 2019 e le entrate totali. Tutte e tre le categorie devono essere visualizzate a livello di cliente. Ricordate che state lavorando per una società di elaborazione di carte di credito, quindi le entrate corrispondono all'importo della transazione. È inoltre necessario assegnare le categorie al cliente; se il cliente ha realizzato un fatturato totale di 1.000.000 dollari o più, viene classificato come "Platino". Se il fatturato totale è inferiore a 1.000.000 di dollari, il cliente deve essere classificato come "Oro". Ecco la query: SELECT cu.NIN, cu.first_name, cu.last_name, SUM(ct.amount) AS total_revenue_per_customer, CASE WHEN SUM(ct.amount) >= 1000000 THEN 'Platinum' WHEN SUM(ct.amount) < 1000000 THEN 'Gold' END AS customer_category, SUM(CASE WHEN ct.date >= '2019-01-01' AND ct.date < '2020-01-01' THEN ct.amount ELSE 0 END) AS revenue_2019, SUM(CASE WHEN ct.date >= '2020-01-01' AND ct.date < '2021-01-01' THEN ct.amount ELSE 0 END) AS revenue_2020 FROM card_transaction ct JOIN card_number cn ON ct.card_number_id = cn.id JOIN customer cu ON cn.customer_id = cu.id GROUP BY cu.NIN, cu.first_name, cu.last_name ORDER BY total_revenue_per_customer DESC; Cominciamo con la parte più semplice: la query seleziona le colonne NIN, first_name e last_name dalla tabella customer. Poi somma gli importi, che saranno le entrate totali. Segue la categorizzazione dei clienti tramite un'istruzione CASE WHEN. La prima WHEN assegna la categoria "Platino", mentre l'altra assegna la categoria "Oro". Queste dichiarazioni sono chiuse da END, e questa nuova colonna si chiamerà customer_category. Successivamente, ho dovuto specificare le condizioni per le colonne che conterranno i numeri relativi al fatturato 2019 e al fatturato 2020. Per farlo, ho utilizzato ancora una volta l'istruzione CASE WHEN. Per le entrate del 2019, la condizione è che le date siano uguali a 2019-01-01 o superiori/nuove, ma inferiori/superiori al 2020-01-01. Questa colonna è denominata revenue_2019. Lo stesso principio viene applicato alla creazione della colonna revenue_2020. Per ottenere i dati, è necessario unire tre tabelle: card_transaction, card_number, e customer. A tutte e tre le tabelle sono associati degli alias. Alla fine della query, i dati vengono raggruppati in base alle colonne NIN, first_name e last_name perché si vogliono i dati a livello di cliente. Inoltre, i risultati sono ordinati in base al fatturato totale in ordine decrescente per avere un aspetto più gradevole. Ecco la tabella sexy che vi renderà famosi tra i geek dei dati: NINfirst_namelast_nametotal_revenue_per_customercustomer_categoryrevenue_2019revenue_2020 116-17-3179EvenSturt1,098,891.00Platinum602,075.43496,815.57 654-50-1963KorieHeims1,091,108.71Platinum536,126.43554,982.28 675-95-5293BrierDrillingcourt1,058,022.84Platinum461,799.16596,223.68 568-26-1849MargetteHenlon1,040,565.01Platinum525,759.81514,805.20 836-72-0333NikolaosKolakowski1,024,073.74Platinum512,434.92511,638.82 642-47-8286JudeKnivett994,881.03Gold534,644.07460,236.96 552-56-0279LilliLayson991,257.18Gold416,496.63574,760.55 405-45-9879NinnetteCockitt965,413.18Gold516,239.21449,173.97 487-13-1311TarranceAngrock946,170.32Gold472,225.09473,945.23 254-88-4824LeonSouter944,216.96Gold528,915.58415,301.38 Vi piace usare le query di Advanced SQL nell'analisi dei dati finanziari? Ho inteso mostrare pratiche SQL relativamente avanzate che consentono di analizzare i dati finanziari e creare report. Non si tratta di tutto ciò che SQL può fare, ma forse vi ho fatto interessare ad alcune funzioni SQL avanzate. Tutti e sei gli esempi si basano sulla mia esperienza di analista di dati. Ho realizzato molti report di questo tipo nella mia vita e sarei stato entusiasta se avessi conosciuto tutte queste possibilità di SQL quando ho iniziato a lavorare; sarebbe stato molto più facile per me. Non è mai troppo tardi per imparare l'SQL avanzato, ma è sempre meglio iniziare prima. Se ne avete la possibilità, iscrivetevi al corso Advanced SQL ; vi ripagherà in futuro. Tags: sql esercizi online analisi dati