题目2:使用递归CTE分析产品层级关系
有两个表:
- product_categories (category_id, category_name, parent_category_id)
- products (product_id, product_name, category_id, price)
- 其中parent_category_id为NULL表示顶级类别。
-- 使用递归CTE分析产品层级关系
use niuke;DROP TABLE IF EXISTS product_categories, products;-- 创建产品类别表
CREATE TABLE product_categories (category_id INT PRIMARY KEY,category_name VARCHAR(100) NOT NULL,parent_category_id INT NULL,FOREIGN KEY (parent_category_id) REFERENCES product_categories(category_id)
);-- 创建产品表
CREATE TABLE products (product_id INT PRIMARY KEY,product_name VARCHAR(100) NOT NULL,category_id INT NOT NULL,price DECIMAL(10, 2) NOT NULL,FOREIGN KEY (category_id) REFERENCES product_categories(category_id)
);-- 插入顶级类别
INSERT INTO product_categories (category_id, category_name, parent_category_id) VALUES
(1, '电子产品', NULL),
(2, '服装', NULL),
(3, '家居用品', NULL),
(4, '食品', NULL);-- 插入子类别
INSERT INTO product_categories (category_id, category_name, parent_category_id) VALUES
(5, '手机', 1),
(6, '笔记本电脑', 1),
(7, '电视', 1),
(8, '男装', 2),
(9, '女装', 2),
(10, '童装', 2),
(11, '厨房用品', 3),
(12, '床上用品', 3),
(13, '生鲜食品', 4),
(14, '零食', 4),
(15, '饮料', 4);-- 插入产品数据
INSERT INTO products (product_id, product_name, category_id, price) VALUES
(1, 'iPhone 15', 5, 7999.00),
(2, '华为Mate 60', 5, 5999.00),
(3, '小米14', 5, 3999.00),
(4, 'MacBook Pro 16寸', 6, 18999.00),
(5, '联想ThinkPad X1', 6, 9999.00),
(6, '索尼4K OLED电视', 7, 12999.00),
(7, '男士休闲衬衫', 8, 299.00),
(8, '男士牛仔裤', 8, 399.00),
(9, '女士连衣裙', 9, 499.00),
(10, '女士高跟鞋', 9, 599.00),
(11, '儿童T恤', 10, 99.00),
(12, '儿童运动鞋', 10, 199.00),
(13, '不粘锅', 11, 199.00),
(14, '四件套', 12, 399.00),
(15, '新鲜牛肉', 13, 89.90),
(16, '薯片', 14, 9.90),
(17, '可乐', 15, 3.50),
(18, '矿泉水', 15, 2.00);
展示所有类别的层级关系,包括每个类别的深度和路径
递归CTE由两部分组成:
- 基础部分:获取层级结构的起点(顶级类别)
- 递归部分:通过连接操作不断获取下一级类别
-- 1. 展示类别层级结构
WITH RECURSIVE category_hierarchy AS (-- 基本查询:获取顶级类别SELECT category_id,category_name,parent_category_id,0 AS depth,category_name AS pathFROM product_categoriesWHERE parent_category_id IS NULLUNION ALL-- 递归查询:获取下级类别SELECT c.category_id,c.category_name,c.parent_category_id,ch.depth + 1 AS depth,concat( ch.path, ' > ' , c.category_name) AS pathFROM product_categories cJOIN category_hierarchy ch ON c.parent_category_id = ch.category_id
)
SELECT * FROM category_hierarchy ORDER BY path;
基础查询部分
- 从
product_categories
表中选择所有顶级类别(parent_category_id IS NULL
) - 初始化
depth
为0,表示这是层级结构的顶层 path
字段初始化为类别名称本身,用于后续构建完整路径
递归查询部分
- 通过
JOIN
操作连接已查询到的类别(category_hierarchy
)和所有类别表 - 连接条件是子类别的
parent_category_id
等于父类别的category_id
depth
每次递归增加1,表示层级加深path
通过连接父类别的路径和当前类别名称,形成完整路径字符串
统计每个类别(包括其所有子类别)下的产品总数和平均价格
-- 2. 统计每个类别及其子类别的产品信息-- 基础查询:获取所有类别作为起点SELECT category_id,category_name,category_id AS root_category_idFROM product_categoriesUNION ALL-- 递归查询:建立子类别与根类别的映射SELECT c.category_id,c.category_name,ct.root_category_idFROM product_categories cJOIN category_tree ct ON c.parent_category_id = ct.category_id
)
SELECT pc.category_id,pc.category_name,COUNT(DISTINCT p.product_id) AS total_products,ROUND(AVG(p.price), 2) AS avg_price
FROM product_categories pc
LEFT JOIN category_tree ct ON pc.category_id = ct.root_category_id
LEFT JOIN products p ON ct.category_id = p.category_id
GROUP BY pc.category_id, pc.category_name
ORDER BY pc.category_id;
- 递归CTE构建类别树:
- 基础部分:每个类别作为自己的根类别(
root_category_id = category_id
) - 递归部分:子类别继承父类别的
root_category_id
,建立"祖先-后代"关系
- 基础部分:每个类别作为自己的根类别(
- 关联产品数据:
- 通过
LEFT JOIN
确保所有类别都出现在结果中 - 通过
category_tree
将产品关联到其所有上级类别
- 通过
- 统计计算:
COUNT(DISTINCT p.product_id)
:统计不重复产品数量ROUND(AVG(p.price), 2)
:计算平均价格并保留两位小数
- 结果展示:按类别名称排序,使结果更易读
这个问题测试对递归CTE的理解,特别是在处理层级数据时的应用。难点在于正确处理递归关系并通过外部连接确保所有类别都被列出。