前言
在 mysql 中,with
语句(或称为公用表表达式,common table expressions,简称 cte)用于定义一个临时结果集,可以在查询的其他部分中重复引用。通常用在复杂查询中,方便将查询逻辑分解为多个部分,代码更清晰,并且可以重复使用中间结果。
mysql 支持两种类型的 cte:
- 非递归 cte:基本的
with
语句,用于定义一次性计算的结果集。 - 递归 cte:cte 自己引用自己,通常用于分层数据或树状结构的查询。
下面分别介绍它们的用法和一些常见示例。
1. 非递归 cte
非递归 cte 在查询中定义一个固定的结果集,在执行后不会再改变。语法如下:
with cte_name as ( select ... ) select * from cte_name;
示例 1:计算部门员工的平均工资
假设有一个 employees
表,包含员工的 department_id
、name
和 salary
。
with dept_avg_salary as ( select department_id, avg(salary) as avg_salary from employees group by department_id ) select e.name, e.salary, d.avg_salary from employees e join dept_avg_salary d on e.department_id = d.department_id where e.salary > d.avg_salary;
这个查询首先用 with
计算各部门的平均工资(dept_avg_salary
),然后找出工资高于部门平均工资的员工。
示例 2:按条件拆分查询
假设要找到销售额最高的 5 位销售人员,可以使用 cte 进行临时排名:
with ranked_sales as ( select name, sales_amount, row_number() over (order by sales_amount desc) as rank from sales_team ) select name, sales_amount from ranked_sales where rank <= 5;
cte ranked_sales
生成了一个带排名的销售记录表,然后主查询从中提取前五名。
2. 递归 cte
递归 cte 允许在定义时引用自身,常用于层级结构的查询,比如管理层次结构、树形结构等。语法如下:
with recursive cte_name as ( select ... -- 初始查询 union all select ... from cte_name -- 递归查询 ) select * from cte_name;
示例 3:计算阶乘
下面是一个递归 cte 示例,计算 1 到 5 的阶乘。
with recursive factorial_cte as ( select 1 as n, 1 as factorial union all select n + 1, factorial * (n + 1) from factorial_cte where n < 5 ) select * from factorial_cte;
这个 cte 首先定义了 n=1
和 factorial=1
的初始值,然后递归地计算 1 到 5 的阶乘。
示例 4:查询部门的层级结构
假设有一个 departments
表,每个部门都有一个 id
和 parent_id
(指向上级部门)。递归 cte 可以查询从某个部门开始的所有子部门。
with recursive dept_hierarchy as ( select id, name, parent_id from departments where id = 1 -- 从根部门 id 为 1 开始 union all select d.id, d.name, d.parent_id from departments d join dept_hierarchy h on d.parent_id = h.id ) select * from dept_hierarchy;
3. 嵌套 cte 和多 cte 定义
在一个查询中可以定义多个 cte,并在查询的其他部分引用它们。这些 cte 可以相互引用,按顺序处理。
示例 5:多个 cte 的嵌套查询
假设要查询一组数据的中间计算结果,可以使用嵌套 cte:
with initial_sales as ( select salesperson_id, sum(sales_amount) as total_sales from sales group by salesperson_id ), ranked_sales as ( select salesperson_id, total_sales, rank() over (order by total_sales desc) as sales_rank from initial_sales ) select salesperson_id, total_sales, sales_rank from ranked_sales where sales_rank <= 10;
这里,initial_sales
计算每个销售人员的总销售额,ranked_sales
对销售额进行排名,然后主查询获取前十名销售人员。
4. 使用 cte 简化复杂查询逻辑
示例 6:复杂查询的分步计算
假设有一个电商订单系统,要求统计每月每个产品的销售额及增长率。
with monthly_sales as ( select product_id, date_format(order_date, '%y-%m') as month, sum(sales_amount) as total_sales from orders group by product_id, month ), sales_growth as ( select m1.product_id, m1.month, m1.total_sales, (m1.total_sales - m2.total_sales) / m2.total_sales as growth_rate from monthly_sales m1 left join monthly_sales m2 on m1.product_id = m2.product_id and date_format(date_sub(m1.month, interval 1 month), '%y-%m') = m2.month ) select * from sales_growth;
这个查询使用两个 cte:monthly_sales
计算每月的总销售额,sales_growth
计算月销售增长率。
总结
with
语句的多种用法总结如下:
- 非递归 cte 用于分解复杂查询。
- 递归 cte 用于层级数据查询。
- 嵌套 cte 可以组合多个步骤的查询。
- 简化查询逻辑:分解复杂的 sql 逻辑,使查询更清晰易懂。
cte 是复杂查询中不可或缺的工具,有助于使代码简洁且易于维护。
with recursive 举例说明,表结构是id和pid的指向 大概有五层
示例:递归查询部门层级
假设有一个 departments
表,结构如下:
id
: 部门 idname
: 部门名称pid
: 父级部门 id(顶级部门的pid
为null
)
表中有五层嵌套的部门数据:
id | name | pid |
---|---|---|
1 | 公司 | null |
2 | 技术部 | 1 |
3 | 市场部 | 1 |
4 | 开发组 | 2 |
5 | 测试组 | 2 |
6 | 前端开发 | 4 |
7 | 后端开发 | 4 |
8 | 大客户市场部 | 3 |
9 | 中小客户市场部 | 3 |
递归 cte 查询:获取指定部门的所有下级部门
我们可以使用递归 cte 从根部门(例如公司层级的 id=1
)开始,查询所有子部门并显示层级关系。
with recursive dept_hierarchy as ( -- 初始查询,获取顶级部门(这里我们从 id=1 的公司开始) select id, name, pid, 1 as level from departments where id = 1 -- 这里可以更改为要查询的根部门的 id union all -- 递归查询:找到上级部门(父级)的下一级部门 select d.id, d.name, d.pid, h.level + 1 as level from departments d join dept_hierarchy h on d.pid = h.id ) select * from dept_hierarchy;
查询结果解释
这个递归 cte 分为两部分:
- 初始查询:
select id, name, pid, 1 as level
,从指定的部门(id=1)开始,将其层级设为1。 - 递归查询:从上级部门的
id
(即h.id
)出发,查找其所有下级部门,并将level
加 1,这样层级关系会递归增长,直到没有下级部门。
执行后,结果显示部门的层级关系:
id | name | pid | level |
---|---|---|---|
1 | 公司 | null | 1 |
2 | 技术部 | 1 | 2 |
3 | 市场部 | 1 | 2 |
4 | 开发组 | 2 | 3 |
5 | 测试组 | 2 | 3 |
6 | 前端开发 | 4 | 4 |
7 | 后端开发 | 4 | 4 |
8 | 大客户市场部 | 3 | 3 |
9 | 中小客户市场部 | 3 | 3 |
在这个查询中,level
列表示部门的层级,从1开始递增。
总结
到此这篇关于mysql中with的多种用法与常见示例的文章就介绍到这了,更多相关mysql with用法示例内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论