sql >> Database >  >> RDS >> Mysql

MySQL 8 Algemene tabeluitdrukkingen CTE

MySQL 8 ondersteunt algemene tabelexpressies, zowel niet-recursief als recursief. Een CTE (Common Table Expression) is een tijdelijke resultaatset waarnaar u kunt verwijzen in een andere SELECT-, INSERT-, UPDATE- of DELETE-instructie.

Niet-recursieve CTE

Een algemene tabelexpressie (CTE) is net als een afgeleide tabel, maar de declaratie ervan wordt vóór het queryblok geplaatst in plaats van in de FROM-clausule. Met behulp van CTE wordt de subquery slechts één keer geëvalueerd, gemeenschappelijke tabeluitdrukkingen maken het gebruik van benoemde tijdelijke resultaatsets mogelijk, gemeenschappelijke tabeluitdrukkingen worden binnen de instructie gedefinieerd met behulp van de WITH-operator.

Stel dat u de procentuele verandering van de uitkeringen van elk jaar wilt weten ten opzichte van het voorgaande jaar. Zonder CTE moet u twee subquery's schrijven en deze zijn in wezen hetzelfde. MySQL is niet slim genoeg om dat te detecteren en de subquery's worden twee keer uitgevoerd.

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)

Met niet-recursieve CTE wordt de afgeleide query slechts één keer uitgevoerd en opnieuw gebruikt

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)

U merkt misschien dat met CTE de resultaten hetzelfde zijn en de querytijd met 50% verbetert, de leesbaarheid goed is en meerdere keren kan worden geraadpleegd

CTEs can refer to other CTEs:
WITH cte1 AS (SELECT ... FROM ...), cte2 AS (SELECT ... FROM cte1 ...)
SELECT
FROM cte1, cte2 ...

Recursieve CTE's

Een recursieve CTE is een CTE die naar zichzelf verwijst. Daarbij wordt de initiële CTE herhaaldelijk uitgevoerd, waarbij subsets van gegevens worden geretourneerd, totdat het volledige resultaat wordt geretourneerd

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 wordt eenmaal uitgevoerd om de initiële gegevenssubset te maken; recursieve SELECT wordt herhaaldelijk uitgevoerd om subsets van gegevens te retourneren totdat de volledige resultatenset is verkregen. Recursie stopt wanneer een iteratie geen nieuwe rijen genereert.

Stel dat u hiërarchische datatraversal wilt doen om een ​​organigram te maken met de managementketen voor elke werknemer (dat wil zeggen, het pad van CEO naar een werknemer). Gebruik een recursieve CTE! Recursieve CTE's zijn zeer geschikt voor het opvragen van hiërarchische gegevens,

Tabel maken

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)
);

gegevens invoegen om een ​​hiërarchische structuur te krijgen

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

Elke rij die door de recursieve zoekopdracht wordt geproduceerd, vindt alle werknemers die rechtstreeks rapporteren aan een
werknemer die door een vorige rij is geproduceerd. Voor elke dergelijke werknemer bevat de rij de
werknemers-ID, naam en werknemersbeheerketen. De keten is de keten van de manager
met de werknemer-ID aan het einde toegevoegd


  1. Hoe de fout door nul te delen in SQL vermijden?

  2. SQL Server:alle dagen in een datumbereik selecteren, zelfs als er enkele dagen geen gegevens zijn

  3. Converteer een datum naar een andere tijdzone in SQL Server

  4. Voorloopnullen verwijderen uit een veld in een SQL-instructie