什么是 CTE?
在 MySQL 中,每个查询都会生成一个临时结果或关系。为了给这些临时结果集命名,使用了 CTE。
- CTE 是使用WITH子句定义的。
- 使用 WITH 子句,我们可以在一条语句中定义多个 CTE。
- CTE 可以在属于同一 WITH 子句的其他 CTE 中引用,但这些 CTE 应该更早定义。
- 每个 CTE 的范围都存在于定义它的语句中。
递归 CTE是一个子查询,它使用自己的名称来引用自己。
- 递归 CTE 是使用WITH RECURSIVE子句定义的。
- 递归 CTE 应该有一个终止条件。
- 递归 CTE 用于生成和遍历分层或树结构数据的系列。
句法:
WITH RECURSIVE
cte_name [(col1, col2, ...)]
AS ( subquery )
Select col1, col2, .. from cte_name;
cte_name: Name given to recursive subquery written in subquery block.
col1, col2, ...colN: The name given to columns generated by subquery.
subquery: A MySql query that refer to itself using cte_name as its own name.
此处, SELECT 语句中给出的列名应与 list 中提供的名称匹配,后跟cte_name 。
子查询块中提供的递归CTE结构:
Select col1, col2, ... coln from table_name
UNION [ALL, DISTINCT]
Select col1, col2, ...coln from cte_name
WHERE clause
递归 CTE 由一个非递归子查询和一个递归子查询组成 –
- 第一个 select 语句是一个非递归语句,它为结果集提供初始行。
- UNION [ALL, DISTINCT] 用于向先前的结果集中添加额外的行。 ALL 和 DISTINCT 关键字的使用用于在最后一个结果集中包含或消除重复行。
- 第二个选择语句是一个递归语句,它迭代地产生结果集,直到 WHERE 子句中提供的条件为真。
- 每次迭代产生的结果集以上次迭代产生的结果集为基表。
- 当递归选择语句不产生任何额外的行时,递归结束。
例子:
- 考虑以下查询,该查询生成一系列前 5 个奇数 –
询问:WITH RECURSIVE odd_no (sr_no, n) AS ( SELECT 1, 1 union all SELECT sr_no+1, n+2 from odd_no where sr_no < 5 ) SELECT * FROM odd_no;
输出:
+---------+-------+ | sr_no | n | +---------+-------+ | 1 | 1 | | 2 | 3 | | 3 | 5 | | 4 | 7 | | 5 | 9 | +---------+-------+
解释:
上面的查询由两部分组成 – 非递归和递归。
非递归部分 – 它将产生由名为“sr_no”和“n”的两列和单行组成的初始行。
Query: SELECT 1, 1 Output: +---------+-------+ | sr_no | n | +---------+-------+ | 1 | 1 | +---------+-------+
递归部分-
SELECT sr_no+1, n+2 from cte where odd_no < 5
它将向先前的输出添加行,直到满足终止条件即( sr_no < 5 )。
当 sr_no 变为 5 时,条件变为假并且递归终止。
- 考虑下面的“bst”表——
mysql> SELECT * FROM bst order by node; +------+-----------+ | node | parent | +------+-----------+ | 1 | NULL | | 2 | 1 | | 3 | 1 | | 4 | 2 | | 5 | 2 | | 6 | 3 | | 7 | 3 | +------+-----------+
上表“bst”由两列“node”和“parent”组成,它们给出了二叉搜索树中节点的值及其各自的父值。
问题描述:我们必须在给定的“bst”中找到所有节点的路径。
询问:
WITH RECURSIVE cte ( node, path ) AS ( SELECT node, cast ( 1 as char(30) ) FROM bst WHERE parent IS NULL UNION ALL SELECT bst.node, CONCAT ( cte.path, '-->', bst.node ) FROM cte JOIN bst ON cte.node = bst.parent ) SELECT * FROM cte ORDER BY node;
输出:
+------+-----------+ | node | path | +------+-----------+ | 1 | 1 | | 2 | 1-->2 | | 3 | 1-->3 | | 4 | 1-->2-->4 | | 5 | 1-->2-->5 | | 6 | 1-->3-->6 | | 7 | 1-->3-->7 | +------+-----------+
解释:
这里,上面 CTE 中的非递归部分将只给出一行,其中包含一个根节点及其设置为 1 的路径。
SELECT node, cast ( 1 as char(30) ) FROM bst WHERE parent IS NULL Output: +------+-----------+ | node | path | +------+-----------+ | 1 | 1 | +------+-----------+
递归部分-
SELECT bst.node, CONCAT ( cte.path, '-->', bst.node ) FROM cte JOIN bst ON cte.node = bst.parent
递归 SELECT 语句将在 bst 中找到所有节点,其父节点是前一次迭代中产生的节点。
当前一次迭代中产生的节点 ie(leaf node) 不包含 bst 中的任何子节点时,这样的迭代结束。