在MySQL中处理带有父子关系的数据时,经常遇到既要按层级展示又要统计每层数据量的需求。借助递归公用表表达式(CTE),我们可以先把树形结构展开,再结合GROUP BY完成分组后的树形统计。

一、示例表结构
假设有一张分类表,自身通过parent_id关联形成树:
CREATE TABLE category ( id INT PRIMARY KEY, name VARCHAR(50), parent_id INT ); INSERT INTO category VALUES (1, '数码', 0), (2, '手机', 1), (3, '电脑', 1), (4, '安卓手机', 2), (5, 'iOS手机', 2), (6, '笔记本', 3);
二、使用递归CTE展开树形路径
递归CTE包含锚点查询和递归查询两部分,可生成每个节点到根的路径:
WITH RECURSIVE cte_tree AS ( SELECT id, name, parent_id, CAST(name AS CHAR(255)) AS path FROM category WHERE parent_id = 0 UNION ALL SELECT c.id, c.name, c.parent_id, CONCAT(t.path, ' > ', c.name) FROM category c INNER JOIN cte_tree t ON c.parent_id = t.id ) SELECT * FROM cte_tree;
三、分组后的树形统计
如果另有一张商品表关联分类,可统计每个分类下的商品数,并保留树形层级:
CREATE TABLE product ( id INT PRIMARY KEY, category_id INT, title VARCHAR(50) ); INSERT INTO product VALUES (1,4,'安卓机A'),(2,4,'安卓机B'),(3,5,'iPhone'), (4,6,'轻薄本'),(5,6,'游戏本'); WITH RECURSIVE cte_tree AS ( SELECT id, name, parent_id, CAST(name AS CHAR(255)) AS path FROM category WHERE parent_id = 0 UNION ALL SELECT c.id, c.name, c.parent_id, CONCAT(t.path, ' > ', c.name) FROM category c INNER JOIN cte_tree t ON c.parent_id = t.id ) SELECT t.path, COUNT(p.id) AS product_count FROM cte_tree t LEFT JOIN product p ON p.category_id = t.id GROUP BY t.id, t.path ORDER BY t.path;
四、结果说明
上述查询会输出类似下面的分层汇总:
| path | product_count |
|---|---|
| 数码 | 0 |
| 数码 > 手机 | 0 |
| 数码 > 手机 > 安卓手机 | 2 |
| 数码 > 手机 > iOS手机 | 1 |
| 数码 > 电脑 > 笔记本 | 2 |
通过递归CTE先把层级铺开,再用LEFT JOIN和GROUP BY就能实现分组后的树形结构统计,不必在应用代码里递归求和。
注意:MySQL 8.0及以上版本才支持WITH RECURSIVE语法,老版本需用存储过程模拟。
实际业务中,你还可以在递归部分增加层级深度字段,方便前端控制缩进或做权限过滤。