22nd Jun 2023 Tempo di lettura: 11 minuti Come scrivere un CTE ricorsivo in SQL Server Nicole Darnley cte server sql ricorsione Indice Nozioni di base sulle CTE in SQL Server Come utilizzare le CTE ricorsive in SQL Server Suggerimenti per la scrittura di CTE ricorsivi in SQL Server 1. Iniziare con il membro di ancoraggio 2. Assicurarsi che il numero di colonne corrisponda 3. Sempre UNION ALL 4. Attenzione ai cicli infiniti! CTE ricorsivi in SQL Server - Altri esempi Esempio 1: CTE ricorsiva per una rete di trasporti Esempio 2: Uso di CTE ricorsivi per le dipendenze dei task in un progetto Per saperne di più sulle CTE ricorsive in SQL Server Guida alla comprensione e all'uso dei CTE ricorsivi in SQL Server SQL Server offre molti strumenti potenti per lavorare con i dati, tra cui le Common Table Expressions (CTE). Una CTE è un insieme di risultati temporaneo con nome che può essere referenziato all'interno di un'istruzione SELECT, INSERT, UPDATE, o DELETE. Le CTE possono suddividere le query più lunghe in parti di logica più piccole e gestibili, rendendole più leggibili. SQL Server offre espressioni di tabella comuni ricorsive. Una Common Table Expression (CTE) ricorsiva in SQL Server consente di eseguire query ricorsive su strutture di dati gerarchiche o grafiche, come organigrammi, alberi genealogici, reti di trasporto, ecc. Le query ricorsive vengono utilizzate per eseguire un ciclo di relazioni tra gli elementi dei dati. In questo articolo, inizieremo con la sintassi CTE standard e poi esploreremo come scrivere CTE ricorsive in SQL Server. Un ottimo modo per approfondire la conoscenza delle CTE ricorsive è seguire il nostro corso su Recursive Queries in MS SQL Server. Vi insegnerà a padroneggiare il tipo di query più impegnativo e a organizzare in modo efficace le query SQL più lunghe. Nozioni di base sulle CTE in SQL Server Per prima cosa, esaminiamo le basi delle CTE. Una CTE inizia con la clausola WITH, seguita dal nome della CTE e dall'istruzione SELECT che la definisce. La sintassi di una CTE è la seguente: WITH cte_name AS ( SELECT column1, column2, … FROM table … ) SELECT … FROM cte_name, …; Cominciamo dall'inizio. La prima cosa da fare è dare un nome alla nostra CTE, cte_name. Questa CTE seleziona alcune colonne (column1, column2, ...) da table. Il resto della query (la query esterna) può fare riferimento alla CTE e alle sue colonne come se la CTE fosse una normale tabella. Si può pensare a una CTE come a una vista temporanea a cui fare riferimento nella query esterna, come stiamo facendo nell'esempio precedente. Supponiamo di voler scrivere una query che restituisca gli acquisti gestiti dai cinque dipendenti con lo stipendio più alto nel reparto vendite. Le tabelle che utilizzeremo in questo esempio si chiamano employees (contiene le colonne employee_id, employee_name e salary) e purchase (contiene le colonne id, date, customer_id e total_amount). WITH top_5_sales_employees AS ( SELECT TOP 5 employee_id, employee_name FROM employees WHERE department_id = ‘Sales’ ORDER BY salary DESC ) SELECT p.id AS purchase_id, p.date, p.total_amount, e.id AS employee_id e.employee_name FROM top_5_sales_employees e JOIN purchase p ON p.employee_id = e.id; Questa CTE inizia con la parola chiave WITH e il nome della CTE, top_5_sales_employees. Tra le parentesi, si selezionano le colonne che si desidera includere nella CTE e si specificano le condizioni della query nella clausola WHERE. Infine, si utilizza l'istruzione SELECT per selezionare le colonne appropriate dalla CTE come se fosse una normale tabella. Per saperne di più su cosa sono le CTE e su come si scrivono, consultate il nostro articolo Cos'è una CTE in SQL Server? Se cercate altri esempi di CTE, li troverete in questo articolo. Come utilizzare le CTE ricorsive in SQL Server Che cos'è un CTE ricorsivo in SQL Server? Un CTE ricorsivo è un tipo di CTE che fa riferimento a se stesso nell'istruzione SELECT, creando un ciclo. Le CTE ricorsive sono utilizzate per attraversare strutture di dati gerarchiche, come diagrammi organizzativi o reti. Supponiamo di avere una tabella chiamata employees con colonne per il nome del dipendente, il reparto e il manager. Questo è mostrato nella tabella seguente, dove ogni record include un dipendente e il suo responsabile all'interno dell'organizzazione. idnamedepartmentmanager_idmanager_name 124John DoeIT135Jane Miller 135Jane MillerHR146Sarah Smith 146Sarah SmithCEONULLNULL A colpo d'occhio, è abbastanza facile vedere chi fa capo a chi e come si presenta la gerarchia organizzativa. Tuttavia, se avessimo centinaia di dipendenti, sarebbe molto più difficile dare un senso ai dati. Possiamo utilizzare una CTE ricorsiva per generare un albero gerarchico dei dipendenti dell'azienda. Per fare ciò, si deve eseguire questa query: WITH employee_manager_cte AS ( SELECT id, name, department, manager_id, manager_name, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL SELECT e.id, e.name, e.department, e.manager_id, e.manager_name, level + 1 FROM employees e INNER JOIN employee_manager_cte r ON e.manager_id = r.id ) SELECT * FROM employee_manager_cte; Analizziamo questa query passo per passo. Innanzitutto, definiamo la CTE ricorsiva con il nome employee_manager_cte. Selezioniamo le colonne che vogliamo includere nella query: id, name, department, manager_id, manager_name e level. La colonna level viene utilizzata per tracciare la profondità dell'albero. Inizieremo con il livello 1; man mano che si procede nel ciclo, questo numero aumenterà. La sezione prima di UNION ALL viene chiamata membro di ancoraggio. Nel membro di ancoraggio iniziamo il nostro ciclo. Nel nostro esempio, selezioniamo tutti i dipendenti il cui manager è NULL. Nel nostro organigramma, si tratta dei dipendenti che si trovano in cima alla lista. In questo caso, c'è un solo dipendente a questo livello: Sarah Smith, l'amministratore delegato. Il pezzo dopo UNION ALL è chiamato membro ricorsivo. Nel membro ricorsivo, si aggiungono nuove righe a quelle già calcolate. Nel nostro esempio, uniamo la tabella employees con la CTE employee_manager_cte sulla colonna manager_id. Questo crea un ciclo che attraversa l'albero dall'alto verso il basso. Aggiungiamo 1 alla colonna level per tenere traccia della profondità di ogni nodo. Infine, si selezionano tutte le colonne della CTE employee_manager_cte. Quando si esegue questa query, SQL Server elabora prima il membro di ancoraggio, che seleziona Sarah Smith come radice dell'albero. Quindi elabora il membro ricorsivo, che unisce Sarah Smith al suo rapporto diretto (Jane Miller). Quindi unisce Jane Miller con il suo rapporto diretto (John Doe) e John Doe con il suo rapporto diretto (nessuno). Poiché non ci sono più righe da aggiungere al set di risultati, SQL Server interrompe l'elaborazione della CTE e restituisce il risultato finale. Ecco come appare il set di risultati: idnamedepartmentmanager_idmanagerlevel 146Sarah SmithCEONULLNULL1 135Jane MillerHR146Sarah Smith2 124John DoeIT135Jane Miller3 Un altro ottimo articolo esplicativo che potete consultare è Che cos'è una CTE ricorsiva in SQL? Qui troverete altri esempi pratici di CTE ricorsive in SQL. Suggerimenti per la scrittura di CTE ricorsivi in SQL Server 1. Iniziare con il membro di ancoraggio Il membro di ancoraggio è il punto di partenza della CTE ricorsiva. È la parte della query che definisce il caso base, ovvero la prima serie di righe che verranno restituite. Nel nostro esempio di organigramma, si tratta del livello più alto di gestione. Assicurarsi che il membro di ancoraggio restituisca tutte le colonne necessarie nel set di risultati finale. 2. Assicurarsi che il numero di colonne corrisponda Il membro ricorsivo e i membri di ancoraggio devono avere lo stesso numero di colonne e gli stessi tipi di dati corrispondenti: In una CTE ricorsiva, il membro ricorsivo fa riferimento alla CTE stessa, quindi è importante assicurarsi che il membro ricorsivo e il membro di ancoraggio abbiano lo stesso numero di colonne e gli stessi tipi di dati nelle colonne corrispondenti. 3. Sempre UNION ALL In SQL Server è possibile utilizzare UNION ALL solo per combinare un membro di ancoraggio e un membro ricorsivo: Quando si combinano il membro di ancoraggio e il membro ricorsivo, è necessario utilizzare UNION ALL e non UNION. UNION ALL conserva tutte le righe, compresi i duplicati, mentre UNION rimuove i duplicati. UNION non è consentita nelle query ricorsive in SQL Server. 4. Attenzione ai cicli infiniti! Assicuratevi di scrivere un controllo di terminazione nella vostra query. Un controllo di terminazione è una condizione che impedisce al CTE ricorsivo di andare in loop all'infinito. Senza un controllo di terminazione, per impostazione predefinita la query eseguirà un massimo di 100 ricorsioni e poi darà errore. Il controllo di terminazione è tipicamente incluso nella clausola WHERE del membro ricorsivo e specifica quando la ricorsione deve terminare. Seguendo questi suggerimenti, è possibile scrivere CTE ricorsive efficienti ed efficaci che aiutano a risolvere problemi gerarchici complessi in SQL Server. Per ulteriori dettagli, consultate questo articolo sulle migliori pratiche CTE. CTE ricorsivi in SQL Server - Altri esempi Esempio 1: CTE ricorsiva per una rete di trasporti Un altro esempio di utilizzo di una CTE ricorsiva è la rappresentazione di una rete di trasporti. Supponiamo di avere una tabella chiamata routes che contiene informazioni sui percorsi di trasporto tra le città. La tabella include la città di origine, la città di destinazione e la distanza tra le città. Vogliamo scrivere una query che restituisca tutte le città raggiungibili da una data città di partenza, insieme alla distanza totale da ciascuna città. I nostri dati si trovano in una tabella chiamata routes: source_citydestination_citydistance New YorkBoston215 New YorkPhiladelphia95 PhiladelphiaWashington140 BostonChicago985 WashingtonAtlanta640 AtlantaMiami660 Ecco la query: WITH recursive_cte AS ( SELECT source_city, destination_city, distance, source_city AS visited_cities FROM routes WHERE source_city = ‘New York’ UNION ALL SELECT r.source_city, r.destination_city, r.distance + rc.distance, rc.visited_cities + ‘,’ + r.destination_city FROM routes r INNER JOIN recursive_cte rc ON r.source_city = rc.destination_city WHERE rc.distance < 2000 AND CHARINDEX(',' + r.destination_city + ',', ',' + rc.visited_cities + ',') = 0 ) SELECT destination_city, distance FROM recursive_cte Questa CTE ricorsiva inizia con il membro di ancoraggio, che seleziona tutti i percorsi che partono da New York. Nel membro ricorsivo, uniamo la tabella dei percorsi con la CTE recursive_cte sulla colonna source_city per trovare tutte le città raggiungibili da New York. Abbiamo aggiunto una nuova colonna chiamata visited_cities che memorizza l'elenco delle città visitate come stringa separata da virgole. Nel membro di ancoraggio della CTE inizializziamo questa colonna impostandola sulla città di origine. Nel membro ricorsivo, si concatena la città corrente all'elenco delle città visitate e si controlla se la città di destinazione è già stata visitata utilizzando la funzione CHARINDEX. Se la città di destinazione non è stata visitata, la aggiungiamo all'elenco delle città visitate e continuiamo la ricorsione. Continuiamo ad aggiungere righe all'insieme di risultati fino a quando non ci sono più città da aggiungere o fino a quando non viene raggiunto il controllo di terminazione. L'insieme dei risultati mostra tutte le città raggiungibili da New York e la distanza totale da ciascuna città. destination_citydistance Boston215 Philadelphia95 Chicago1200 Washington235 Atlanta875 Miami1535 Esempio 2: Uso di CTE ricorsivi per le dipendenze dei task in un progetto Un altro scenario in cui si potrebbe usare una CTE ricorsiva è quello di capire le dipendenze dei compiti di un progetto. Supponiamo di avere una tabella chiamata tasks che memorizza informazioni sulle attività di un progetto, tra cui task_id, task_name, l'ID dell'attività da cui dipende (depends_on_task_id) e time_required per completare l'attività. Vogliamo scrivere una query che calcoli il tempo totale necessario per completare un determinato compito, compresi tutti i compiti dipendenti. I dati sono mostrati di seguito: task_idtask_namedepends_on_task_idtime_required 1DesignNULL5 2Development110 3Testing25 4Documentation13 5Deployment32 6MarketingNULL7 Scriviamo una query che estragga il tempo totale richiesto per l'attività di sviluppo e le sue dipendenze. WITH recursive_cte AS ( SELECT task_id, task_name, depends_on_task_id, time_required FROM tasks WHERE task_id = 2 UNION ALL SELECT t.task_id, t.task_name, depends_on_task_id, t.time_required + rc.time_required FROM tasks t INNER JOIN recursive_cte rc ON t.depends_on_task_id = rc.task_id WHERE rc.total_time < 20) SELECT task_name, time_required as total_time FROM recursive_cte GROUP BY task_name; Questa CTE ricorsiva inizia con il membro di ancoraggio, che seleziona la riga nella tabella tasks con task_id = 2 (Sviluppo). Nel membro ricorsivo, si unisce alla tabella tasks con la CTE recursive_cte sulle colonne depends_on_task_id e task_id per trovare tutte le attività che dipendono da Sviluppo. Si calcola il tempo totale richiesto per ogni attività sommando il tempo richiesto per l'attività corrente al tempo totale richiesto per le attività precedenti. Infine, interroghiamo i risultati per task_name e time_required per ogni attività. Ecco i risultati: Task_nameTotal_time Development10 Testing15 Deployment17 La colonna task_name mostra il nome di ogni attività e la colonna total_time mostra il tempo totale richiesto per completare l'attività e tutte le attività precedenti. Ad esempio, la prima riga mostra che il tempo totale richiesto per completare Sviluppo e tutte le sue dipendenze è 15, che è la somma del tempo richiesto per Sviluppo (10) e Test (5). Le altre righe mostrano lo stesso concetto, con le colonne task_name e total_time che indicano il nome di ogni attività e il relativo tempo totale richiesto. Per saperne di più sulle CTE ricorsive in SQL Server Le CTE ricorsive in SQL Server possono essere utili in diversi scenari, ad esempio: Gerarchie di dipendenti: Una CTE ricorsiva può essere utilizzata per attraversare un albero di dipendenti e dei loro manager per calcolare la retribuzione totale di tutti i dipendenti in un determinato ramo. Categorie di prodotti: Una CTE ricorsiva può essere utilizzata per attraversare un albero di categorie di prodotti per trovare tutti i prodotti di una determinata categoria e delle sue sottocategorie. Reti sociali: Una CTE ricorsiva può essere usata per attraversare un grafo di connessioni di social network per trovare tutti gli amici di un dato utente e le loro connessioni. Dipendenze da attività: Una CTE ricorsiva può essere utilizzata per attraversare un albero di dipendenze di attività per calcolare il tempo necessario per completare un progetto. In generale, qualsiasi scenario in cui i dati sono organizzati gerarchicamente o in una struttura a grafo può potenzialmente trarre vantaggio dall'uso di una CTE ricorsiva. Seguendo le migliori pratiche, come iniziare con il membro di ancoraggio, assicurarsi che i membri ricorsivi e di ancoraggio abbiano lo stesso numero di colonne e tipi di dati e scrivere un controllo di terminazione, è possibile scrivere query efficienti e accurate che attraversano strutture di dati gerarchiche e risolvono problemi complessi. Rafforzate la vostra comprensione dei CTE ricorsivi con il corso Recursive Queries in MS SQL Server. I suoi 112 esercizi interattivi vi aiuteranno a diventare padroni di questo argomento avanzato! Tags: cte server sql ricorsione