Mysql
 sql >> Database >  >> RDS >> Mysql

MySQL 8 Espressioni di tabelle comuni CTE

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