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