CTE与递归CTE基础语法
MySQL 8.0引入的公用表表达式(Common Table Expression,CTE)通过WITH子句定义临时结果集,在一条查询中可多次引用。CTE分为非递归CTE和递归CTE两种,非递归CTE本质是可读性更好的子查询别名,递归CTE则支持自引用迭代,是处理层级数据的核心工具。
递归CTE的基本结构包含锚点成员和递归成员,用UNION ALL连接:
WITH RECURSIVE org_tree AS ( -- 锚点查询:起始层级 SELECT id, name, manager_id, 1 AS level FROM employees WHERE manager_id IS NULL UNION ALL -- 递归查询:逐层展开 SELECT e.id, e.name, e.manager_id, ot.level + 1 FROM employees e INNER JOIN org_tree ot ON e.manager_id = ot.id)SELECT * FROM org_tree;
锚点查询返回递归起点,递归成员引用CTE自身不断扩展结果集,直到递归成员返回空结果集时终止。MySQL默认递归深度上限为1000,超过则报错。通过cte_max_recursion_depth系统变量调整上限。
递归CTE实现组织架构树遍历
组织架构树是最典型的递归查询场景。以下示例实现从指定节点向下遍历完整子树:
WITH RECURSIVE sub_tree AS ( SELECT id, name, manager_id, CAST(name AS CHAR(500)) AS path, 1 AS depth FROM employees WHERE id = 100 -- 起始节点 UNION ALL SELECT e.id, e.name, e.manager_id, CONCAT(st.path, ' > ', e.name) AS path, st.depth + 1 AS depth FROM employees e INNER JOIN sub_tree st ON e.manager_id = st.id)SELECT id, name, path, depthFROM sub_treeORDER BY depth, name;
path字段用CONCAT逐层拼接路径字符串,输出层级路径格式。CAST(name AS CHAR(500))确保path字段有足够长度,避免递归层数过多时截断。
递归CTE实现物料BOM展开
制造业BOM(物料清单)的层级展开需要聚合每层子件的数量,是递归CTE的高阶用法:
WITH RECURSIVE bom_expand AS ( -- 锚点:顶级成品 SELECT parent_id, child_id, quantity, child_id AS root_part, quantity AS total_qty, 1 AS level FROM bom WHERE parent_id = 'PRODUCT-A' UNION ALL -- 递归:逐层展开子件 SELECT b.parent_id, b.child_id, b.quantity, be.root_part, be.total_qty * b.quantity AS total_qty, be.level + 1 AS level FROM bom b INNER JOIN bom_expand be ON b.parent_id = be.child_id)SELECT child_id AS part_number, SUM(total_qty) AS required_quantity, MAX(level) AS max_depthFROM bom_expandGROUP BY child_idORDER BY max_depth;
关键点在于total_qty的计算:每层子件数量=父件总数量乘以本层单位用量。递归过程中逐层累乘,最终GROUP BY汇总每个物料的总需求量。
递归CTE性能优化策略
递归CTE的性能瓶颈集中在递归成员的执行频率上。每次递归迭代都会执行一次JOIN操作,N层深度意味着N次JOIN。优化手段:
1. 在递归JOIN字段上建立索引。上述示例中manager_id和child_id必须建索引,否则每次递归迭代都是全表扫描:
CREATE INDEX idx_emp_manager ON employees(manager_id);CREATE INDEX idx_bom_child ON bom(child_id);
2. 限制递归深度。业务场景通常层级有限(组织架构一般不超过10层),用WHERE条件提前终止递归:
-- 限制只查3层WHERE st.depth < 3
3. 避免递归CTE中的排序和聚合。递归成员中包含GROUP BY或ORDER BY会严重拖慢每次迭代,应将聚合操作放到外层查询。
4. 监控递归执行计划。用EXPLAIN ANALYZE查看递归CTE的执行计划,关注迭代次数和单次扫描行数。如果单次迭代扫描行数随递归深度指数增长,说明索引缺失或JOIN条件有问题。
CTE对比传统子查询的性能差异
非递归CTE在MySQL 8.0.14之前会被优化器内联展开,与子查询性能一致。8.0.14之后optimizer_switch的subquery_to_derived选项会物化CTE,对多次引用同一CTE的查询有性能提升——物化结果只计算一次,后续引用直接读缓存。
递归CTE对比存储过程+临时表方案的优势:单条SQL完成递归,无需创建临时表、无需循环控制逻辑、无需手动清理临时数据。缺点是递归CTE无法在递归过程中做条件分支(IF/CASE),复杂逻辑仍需存储过程实现。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/mysql80-gong-yong-biao-biao-da-shi-cte-di-gui-cha-xun-you/