MySQL 8 supporta espressioni di tabelle comuni, sia non ricorsive che ricorsive, A CTE (Common Table Expression) è un set di risultati temporaneo a cui puoi fare riferimento all'interno di un'altra istruzione SELECT, INSERT, UPDATE o DELETE.
CTE non ricorsivo
Un'espressione di tabella comune (CTE) è proprio come una tabella derivata, ma la sua dichiarazione viene inserita prima del blocco di query anziché nella clausola FROM. Utilizzando CTE, la sottoquery viene valutata solo una volta, le espressioni di tabella comuni consentono l'uso di set di risultati temporanei denominati, le espressioni di tabella comuni sono definite all'interno dell'istruzione utilizzando l'operatore WITH.
Supponiamo di voler scoprire la variazione percentuale dei pagamenti di ogni anno rispetto all'anno precedente. Senza CTE, devi scrivere due sottoquery e sono essenzialmente le stesse. MySQL non è abbastanza intelligente da rilevarlo e le sottoquery vengono eseguite due volte.
SELECT q1.years, q2.years AS next_year, q1.sum1, q2.sum1 AS next_sum, 100 * (q2.sum1 - q1.sum1) / q1.sum1 AS pct FROM (SELECT YEAR(paymentDate) AS years, SUM(amount) AS sum1 FROM payments GROUP BY years) AS q1, (SELECT YEAR(paymentDate) AS years, SUM(amount) AS sum1 FROM payments GROUP BY years) AS q2 WHERE q1.years = q2.years - 1; +-------+-----------+------------+------------+------------+ | years | next_year | sum1 | next_sum | pct | +-------+-----------+------------+------------+------------+ | 2003 | 2004 | 3250217.70 | 4313328.25 | 32.708903 | | 2004 | 2005 | 4313328.25 | 1290293.28 | -70.085901 | +-------+-----------+------------+------------+------------+ 2 rows in set (0.01 sec)
Con CTE non ricorsivo, la query derivata viene eseguita una sola volta e riutilizzata
WITH CTE_NAME AS (SELECT YEAR(paymentDate) AS years, SUM(amount) AS sum1 FROM payments GROUP BY years) SELECT q1.years, q2.years AS next_year, q1.sum1, q2.sum1 AS next_sum,100 * (q2.sum1 - q1.sum1) / q1.sum1 AS pct FROM CTE_NAME AS q1, CTE_NAME AS q2 WHERE q1.years = q2.years - 1; +-------+-----------+------------+------------+------------+ | years | next_year | sum1 | next_sum | pct | +-------+-----------+------------+------------+------------+ | 2003 | 2004 | 3250217.70 | 4313328.25 | 32.708903 | | 2004 | 2005 | 4313328.25 | 1290293.28 | -70.085901 | +-------+-----------+------------+------------+------------+ 2 rows in set (0.00 sec)
Potresti notare che con CTE i risultati sono gli stessi e il tempo di query migliora del 50%, la leggibilità è buona e può essere referenziata più volte
CTEs can refer to other CTEs: WITH cte1 AS (SELECT ... FROM ...), cte2 AS (SELECT ... FROM cte1 ...) SELECT FROM cte1, cte2 ...
CTE ricorsivi
Un CTE ricorsivo è un CTE che fa riferimento a se stesso. In tal modo, il CTE iniziale viene eseguito ripetutamente, restituendo sottoinsiemi di dati, fino a quando non viene restituito il risultato completo
WITH RECURSIVE cte_name AS ( cte_definition -- /* seed SELECT */ UNION ALL cte_definition -- /* "recursive" SELECT */ references cte_name. ) -- Statement using the CTE SELECT * FROM cte_name
Seed SELECT viene eseguito una volta per creare il sottoinsieme di dati iniziale; SELECT ricorsivo viene eseguito ripetutamente per restituire sottoinsiemi di dati fino a ottenere il set di risultati completo. La ricorsione si interrompe quando un'iterazione non genera nuove righe.
Si supponga di voler eseguire l'attraversamento gerarchico dei dati per produrre un organigramma con la catena di gestione per ciascun dipendente (ovvero il percorso dal CEO a un dipendente). Usa un CTE ricorsivo! I CTE ricorsivi sono adatti per interrogare dati gerarchici,
Crea tabella
CREATE TABLE mangeremp ( id INT PRIMARY KEY NOT NULL, name VARCHAR(100) NOT NULL, man_id INT NULL, INDEX (man_id), FOREIGN KEY (man_id) REFERENCES mangeremp (id) );
inserire i dati per ottenere una struttura gerarchica
INSERT INTO mangeremp VALUES (333, "waqas", NULL), # waqas is the CEO (man_id is NULL) (198, "ali", 333), # ali has ID 198 and reports to 333 (waqas) (692, "ahmed", 333), #ahmed report to waqas (29, "oasama", 198), #osama report to ali as alo has ref id 198 (4610, "Mughees", 29), # Mughees report to osama (72, "aslam", 29), (123, "afrooz", 692);
WITH RECURSIVE emp_paths (id, name, path) AS (SELECT id, name, CAST(id AS CHAR(200)) FROM mangeremp WHERE man_id IS NULL UNION ALL SELECT e.id, e.name, CONCAT(ep.path, ',', e.id) FROM emp_paths AS ep JOIN mangeremp AS e ON ep.id = e.man_id ) SELECT * FROM emp_paths ORDER BY path; +------+---------+-----------------+ | id | name | path | +------+---------+-----------------+ | 333 | waqas | 333 | | 198 | ali | 333,198 | | 29 | oasama | 333,198,29 | | 4610 | Mughees | 333,198,29,4610 | | 72 | aslam | 333,198,29,72 | | 692 | ahmed | 333,692 | | 123 | afrooz | 333,692,123 | +------+---------+-----------------+ 7 rows in set (0.00 sec)
SELECT e.id, e.name, CONCAT(ep.path, ',', e.id) FROM emp_paths AS ep JOIN mangeremp AS e ON ep.id = e.man_id ---- recursive query
Ogni riga prodotta dalla query ricorsiva trova tutti i dipendenti che riportano direttamente a un
dipendente prodotto da una riga precedente. Per ciascuno di questi dipendenti, la riga include l'
ID dipendente, il nome e la catena di gestione dei dipendenti. La catena è la catena del manager
con l'ID dipendente aggiunto alla fine