CTE
首先了解一下什么是 CTE,全名 Common Table Expressions
WITH cte1 AS (SELECT a, b FROM table1), cte2 AS (SELECT c, d FROM table2) SELECT b, d FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;
cte1, cte2 為我們定義的CTE,可以在當前查詢中引用,可以看出 CTE 就是一個臨時結果集,和派生表類似
遞歸查詢
先來看下遞歸查詢的語法
WITH RECURSIVE cte_name AS ( SELECT ... -- return initial row set
UNION ALL / UNION DISTINCT
SELECT ... -- return additional row sets
) SELECT * FROM cte;
- 定義一個CTE,這個CTE 最終的結果集就是我們想要的 ”遞歸得到的樹結構",RECURSIVE 代表當前 CTE 是遞歸的
- 第一個SELECT 為 “初始結果集”
- 第二個SELECT 為遞歸部分,利用 "初始結果集/上一次遞歸返回的結果集" 進行查詢得到 “新的結果集”
- 直到遞歸部分結果集返回為null,查詢結束
- 最終UNION ALL 會將上述步驟中的所有結果集合並(UNION DISTINCT 會進行去重),再通過 SELECT * FROM cte; 拿到所有的結果集
遞歸部分不能包括:
- 聚合函數例如 SUM()
- GROUP BY
- ORDER BY
- LIMIT
- DISTINCT
上面的講解可能有點抽象,通過例子慢慢來理解
WITH RECURSIVE cte (n) AS -- 這里定義的n相當於結果集的列名,也可在下面查詢中定義
( SELECT 1
UNION ALL
SELECT n + 1 FROM cte WHERE n < 5 ) SELECT * FROM cte; -- result
+------+
| n |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+------+
- 初始結果集為 n =1
- 這時候看遞歸部分,第一次執行 CTE結果集即是 n =1,條件發現並不滿足 n < 5,返回 n + 1
- 第二次執行遞歸部分,CTE結果集為 n = 2,遞歸... 直至條件不滿足
- 最后合並結果集
EXAMPLE
最后來看一個樹結構的例子
CREATE TABLE `c_tree` ( `id` int(11) NOT NULL AUTO_INCREMENT, `cname` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `parent_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; mysql> select * from c_tree; +----+---------+-----------+
| id | cname | parent_id |
+----+---------+-----------+
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 3 | 0 |
| 4 | 1-1 | 1 |
| 5 | 1-2 | 1 |
| 6 | 2-1 | 2 |
| 7 | 2-2 | 2 |
| 8 | 3-1 | 3 |
| 9 | 3-1-1 | 8 |
| 10 | 3-1-2 | 8 |
| 11 | 3-1-1-1 | 9 |
| 12 | 3-2 | 3 |
+----+---------+-----------+
mysql>
WITH RECURSIVE tree_cte as ( select * from c_tree where parent_id = 3
UNION ALL
select t.* from c_tree t inner join tree_cte tcte on t.parent_id = tcte.id ) SELECT * FROM tree_cte; +----+---------+-----------+
| id | cname | parent_id |
+----+---------+-----------+
| 8 | 3-1 | 3 |
| 12 | 3-2 | 3 |
| 9 | 3-1-1 | 8 |
| 10 | 3-1-2 | 8 |
| 11 | 3-1-1-1 | 9 |
+----+---------+-----------+
- 初始結果集R0 = select * from c_tree where parent_id = 3
- 遞歸部分,第一次 R0 與 c_tree inner join 得到 R1
- R1 再與 c_tree inner join 得到 R2
- ...
- 合並所有結果集 R0 + ... + Ri