18th Jul 2022 Tempo di lettura: 9 minuti Come utilizzare 2 CTE in un'unica query SQL Tihomir Babic sql imparare sql CTE Indice 2 CTE, 1 query SQL, 0 problemi Ma è possibile utilizzare una CTE all'interno di un'altra CTE? Utilizzo di CTE ricorsivi con CTE non ricorsivi La combinazione di due o più CTE funziona davvero Vi siete mai chiesti come utilizzare più CTE in un'unica query SQL? Leggete questo articolo e scoprite le CTE ricorsive. Dopo aver appreso le comuni espressioni di tabella o CTE, viene spontaneo chiedersi : "Posso usare più CTE in un'unica query?". Sì, è possibile! E lo si può fare facilmente, soprattutto se si ha già una conoscenza di base delle CTE. Sia che conosciate un po' le CTE, sia che siate completamente nuovi al mondo delle CTE, leggere cosa sono le CTE è sempre un buon inizio. Quanto appreso in questo articolo può essere messo in pratica nel corso LearnSQL.it Recursive Queries, che è stato il corso del mese di agosto. Inizierò spiegando come utilizzare due CTE in una query. Poi vi insegnerò a usare le CTE in cui la seconda CTE fa riferimento alla prima. Per dare un po' di pepe a questo articolo, concluderò mostrandovi due query con CTE, una delle quali ricorsiva. 2 CTE, 1 query SQL, 0 problemi Immaginiamo che ci sia una tabella denominata logins che memorizza login dati. Contiene le colonne: id - L'ID del login. username - L'utente che ha effettuato l'accesso. login_date - La data dell'accesso. login_start - Quando l'utente ha effettuato l'accesso. login_end - Quando l'utente si è disconnesso. Se si osserva la tabella, si noterà che ogni nome utente compare almeno una volta. idusernamelogin_datelogin_startlogin_end 1JohnXYZ2020-07-037:02:547:08:12 2JohnXYZ2020-07-059:03:2111:08:04 3JohnXYZ2020-07-1214:08:1214:52:13 4Sarah822020-07-0814:05:1215:01:56 5Sarah822020-07-0816:22:4717:13:00 6SugarCane1232020-07-0218:22:4718:42:15 7SugarCane1232020-07-2510:12:5312:52:44 Se il compito è quello di calcolare il tempo medio (in minuti) in cui ogni utente ha effettuato l'accesso, come si può fare utilizzando le CTE? Avrete bisogno di due CTE, il cui codice sarà simile a questo: WITH distinct_user AS ( SELECT DISTINCT username FROM logins), minutes_logged AS ( SELECT username, DATEDIFF (minute, login_start, login_end) AS minutes FROM logins) SELECT u.username, AVG (minutes) AS avg_time_logged FROM distinct_user AS u JOIN minutes_logged AS m ON u.username = m.username GROUP BY u.username; Per prima cosa, ricordiamo che è necessario avere nomi utente distinti perché questo funzioni. Ora iniziamo a scrivere la prima CTE! Non c'è niente di strano: come per ogni CTE, la si definisce con WITH; non per niente si chiamano query WITH! Quello che segue è il nome della CTE; in questo caso, la prima CTE si chiama distinct_user. La si fa seguire dalla parola chiave AS e poi si definisce semplicemente la normale istruzione SELECT all'interno delle parentesi. Questa istruzione SELECT seleziona semplicemente i nomi utente unici dalla tabella logins. Una volta che abbiamo nomi di utenti distinti, dobbiamo calcolare per quanto tempo l'utente è stato connesso. Questa è la parte critica: scrivere la seconda CTE. La mia seconda CTE si chiama minutes_logged. Noterete che non c'è WITH prima della seconda CTE. Questo è estremamente importante! Dopo aver definito la prima CTE, questa è separata dalla seconda solo dalla virgola, cioè si scrive WITH solo una volta. Dopodiché, non importa quante CTE si definiscono; è importante solo separarle con la virgola e iniziare ogni CTE con il suo nome. Analizziamo ora cosa fa il CTE minutes_logged. Per prima cosa prende la colonna username dalla tabella logins. Poi calcola la differenza tra login_start e login_end in minuti. Questo nuovo valore verrà mostrato nella colonna minutes. Dovrete calcolare il tempo medio (in minuti) trascorso da ogni utente durante l'accesso. Per questa parte del calcolo, ho usato la query esterna. Essa seleziona il nome utente dalla CTE distinct_user e poi calcola i minuti medi di accesso usando la colonna minutes dalla CTE minutes_logged. Queste due CTE vengono unite utilizzando la colonna username. Infine, il risultato viene raggruppato in base alla stessa colonna, poiché si desidera un risultato a livello di utente. usernameavg_time_logged JohnXYZ58 Sarah8253 SugarCane12390 Ma è possibile utilizzare una CTE all'interno di un'altra CTE? Una domanda semplice merita una risposta semplice: sì, è possibile. Ora che sapete come usare più CTE, scrivere una CTE che fa riferimento a un'altra CTE è solo una variante di ciò che avete imparato. Poiché si tratta di una variante, credo sia meglio mostrarvi come farlo utilizzando un esempio che già conoscete. Torniamo all'esempio precedente. Modificherò un po' il codice per mostrare come scrivere una CTE che faccia riferimento alla prima CTE. Questa è la nuova versione del codice precedente: WITH difference AS ( SELECT username, DATEDIFF (minute, login_start, login_end) AS minutes FROM logins), average_logged AS ( SELECT username, AVG (minutes) AS average FROM difference GROUP BY username) SELECT DISTINCT username, average FROM average_logged; La logica è la stessa del primo esempio, solo che i passaggi sono in ordine different. La prima CTE, difference, calcola i minuti trascorsi con ogni login; è la stessa del primo esempio. Ora che ho i minuti, devo calcolare il tempo medio (in minuti) trascorso da ciascun utente. Per questo utilizzerò la media CTE. Come ricorderete, mettiamo una virgola tra le due CTE e iniziamo la seconda con il suo nome - non c'è bisogno di usare di nuovo WITH! Questa seconda CTE calcola il tempo medio per ogni login; a tale scopo, utilizza la colonna minutes della prima CTE e memorizza il risultato nella colonna average. Per fare riferimento alla CTE difference nella seconda CTE, la si tratta come una tabella: FROM difference. Infine, si raggruppa il risultato in base al nome utente, poiché non si vuole la media di tutti gli utenti. La query esterna seleziona semplicemente le colonne username e media dalla CTE average_logged. In questo modo, si ottiene lo stesso risultato del primo esempio: usernameavg_time_logged JohnXYZ58 Sarah8253 SugarCane12390 Se questi esempi non vi bastano, c'è un articolo che ne fornisce altri per fare pratica. Questo include anche l'uso di più di una CTE. Utilizzo di CTE ricorsivi con CTE non ricorsivi C'è un motivo per cui le CTE vengono insegnate nel nostro corso. Recursive Queries corso. Perché i CTE possono essere anche ricorsivi. Questa è la parte più complicata dell'apprendimento dei CTE. (Presumo che sappiate già cos'è un CTE ricorsivo e come scriverlo. In caso contrario, si veda Query SQL lunga vs. Query SQL ricorsiva per un'introduzione e Fallo in SQL: Recursive SQL Tree Traversal per un esempio approfondito. ) Tuttavia, forse non vi siete resi conto che è possibile utilizzare più di una CTE, anche se una di esse è ricorsiva. Vediamo come fare. Per questo esempio, immaginiamo di voler acquistare un'azienda con i nostri amici. Ci sono quattro opzioni per l'investimento, ognuna delle quali richiede una somma di denaro diversa. Siete ancora in fase di trattativa. Non siete sicuri di quanti amici parteciperanno; la loro partecipazione dipende dalla quantità di denaro che hanno da investire. Tale importo dipende dal numero totale di investitori coinvolti e dall'opzione di investimento scelta. Per aiutarli a decidere, avete deciso di calcolare l'importo richiesto per ogni investitore per tutte e quattro le opzioni di investimento con un numero di investitori da uno a dieci. In questo esempio, abbiamo prima una CTE non ricorsiva e poi una CTE ricorsiva: WITH RECURSIVE investment AS ( SELECT amount 5897645 AS investment_amount UNION SELECT 4536841 AS investment_amount UNION SELECT 3852457 AS investment_amount UNION SELECT 3452115 AS investment_amount ), per_investor AS ( SELECT 0 AS investors_number, 0 AS amount, 0 AS individual_amount UNION SELECT investors_number + 1, investment_amount, investment_amount/(investors_number + 1) FROM investment, per_investor WHERE investors_number < 10) SELECT * FROM per_investor ORDER BY amount, investors_number; La distinzione principale è evidente fin dall'inizio! Quando si scrivono query ricorsive, è necessario iniziare il codice utilizzando WITH RECURSIVE. Come si noterà, non è necessario iniziare con la CTE ricorsiva stessa; in questo esempio, una CTE non ricorsiva viene prima, anche se usiamo WITH RECURSIVE subito prima di definirla. È possibile scrivere le CTE nell'ordine che si desidera; quella ricorsiva può essere la prima o l'ultima. Ma è importante ricordare che se si ha almeno una query ricorsiva, la scrittura di WITH RECURSIVE è obbligatoria. Nell'esempio attuale, la prima CTE (non ricorsiva) si chiama investimento. Poiché non ho una tabella con tutti gli importi degli investimenti, ho deciso di usare la CTE come tabella temporanea. Come si può vedere, i possibili importi degli investimenti sono i seguenti: 5,897,645 4,536,841 3,852,457 3,452,115 Utilizzando UNION con SELECT nel primo CTE, sto virtualmente creando una tabella che contiene le quattro possibilità di investimento. Esse saranno visualizzate nella colonna investment_amount. La tabella risultante per questa CTE è: investment_amount 3,452,115 3,852,457 4,536,841 5,897,645 La seconda CTE è ricorsiva. Tuttavia, non cambia nulla rispetto alla scrittura di due CTE non ricorsive: la virgola separa nuovamente le CTE. Analizziamo un po' la seconda CTE. La prima istruzione SELECT definisce tre colonne che hanno il valore 0: investors_number, amount e individual_amount. Come ho detto, la CTE ricorsiva si riferisce a se stessa, quindi è necessario l'operatore UNION. L'operatore UNION è seguito da una o più istruzioni SELECT, che eseguiranno l'operazione desiderata sulla prima istruzione SELECT. Ciò significa che aggiungerà 1 alla colonna investors_number. Quindi inserirà investment_amount dalla prima CTE nella colonna importo. Dividerà l'importo dell'investimento con il numero di investitori coinvolti; il risultato verrà mostrato nella colonna individual_amount. Eseguirà questa operazione per tutti e quattro gli importi di investimento fino a raggiungere dieci investitori. La query esterna seleziona semplicemente tutti i dati dalla CTE per_investor, ordinandoli in base alle colonne importo e investors_number. Eseguite questa query e godetevi tutte le possibilità che avete calcolato: investors_numberamountindividual_amount 000 13,452,1153,452,115 23,452,1151,726,057 33,452,1151,150,705 43,452,115863,028 53,452,115690,423 63,452,115575,352 73,452,115493,159 83,452,115431,514 93,452,115383,568 103,452,115345,211 13,852,4573,852,457 23,852,4571,926,228 33,852,4571,284,152 43,852,457963,114 53,852,457770,491 63,852,457642,076 73,852,457550,351 83,852,457481,557 93,852,457428,050 103,852,457385,245 14,536,8414,536,841 24,536,8412,268,420 34,536,8411,512,280 44,536,8411,134,210 54,536,841907,368 64,536,841756,140 74,536,841648,120 84,536,841567,105 94,536,841504,093 104,536,841453,684 15,897,6455,897,645 25,897,6452,948,822 35,897,6451,965,881 45,897,6451,474,411 55,897,6451,179,529 65,897,645982,940 75,897,645842,520 85,897,645737,205 95,897,645655,293 105,897,645589,764 Davvero impressionante, non è vero? La combinazione di due o più CTE funziona davvero Le espressioni comuni di tabella SQL sono uno strumento potente. Sono utili se si vuole che il codice sia più leggibile, ma il loro valore non è solo estetico; le loro reali possibilità brillano se si sa come usare diverse CTE in una query o addirittura scrivere una CTE ricorsiva in SQL. Come si è visto, non è difficile combinare due o più CTE. Una volta che lo si sa, si aprono le possibilità di utilizzo delle CTE in varie situazioni. Ora sta a voi trovare i casi in cui dovrete usare le CTE. E fate pratica, pratica, pratica! È sempre consigliabile utilizzare il nostro corso Recursive Queries per questo. Vi fornirà le tabelle su cui esercitarvi, così non dovrete preoccuparvi di creare da soli gli scenari. Non esitate a condividere la vostra esperienza con le CTE SQL nella sezione commenti. Tags: sql imparare sql CTE