很多人第一次看到窗口函数的时候,都会觉得:
sql
这是什么东西?
尤其是:
sql
OVER()
ROW_NUMBER()
RANK()
看起来特别抽象。
但实际上:
窗口函数本质上就是"在保留原数据的基础上做统计"。
这是它和:
sql
GROUP BY
最大的区别。
一、先理解什么是窗口函数
先看一个普通需求。
比如有一张成绩表:
| id | name | score |
|---|---|---|
| 1 | 张三 | 90 |
| 2 | 李四 | 85 |
| 3 | 王五 | 95 |
现在需求:
查询:
- 学生姓名
- 分数
- 排名
这时候:
sql
GROUP BY
其实做不了。
因为:
sql
GROUP BY
会把原数据"压缩"。
而窗口函数:
可以一边保留原数据,一边做统计。
二、窗口函数基本语法
sql
函数名() OVER()
比如:
sql
SELECT
name,
score,
ROW_NUMBER() OVER()
FROM student;
三、OVER() 到底是什么
这个是窗口函数的核心。
你可以把它理解成:
"规定统计范围"
比如:
sql
OVER()
表示:
sql
对整个结果集进行处理
四、第一个窗口函数:ROW_NUMBER()
作用:
给每一行编号。
示例
sql
SELECT
name,
score,
ROW_NUMBER() OVER() AS rn
FROM student;
结果:
| name | score | rn |
|---|---|---|
| 张三 | 90 | 1 |
| 李四 | 85 | 2 |
| 王五 | 95 | 3 |
五、ORDER BY 很重要
窗口函数通常会配合:
sql
ORDER BY
一起使用。
按成绩排名
sql
SELECT
name,
score,
ROW_NUMBER() OVER(
ORDER BY score DESC
) AS rn
FROM student;
结果:
| name | score | rn |
|---|---|---|
| 王五 | 95 | 1 |
| 张三 | 90 | 2 |
| 李四 | 85 | 3 |
六、RANK() 和 ROW_NUMBER() 的区别
这是面试高频。
很多人容易搞混。
数据
| name | score |
|---|---|
| 张三 | 90 |
| 李四 | 90 |
| 王五 | 85 |
1.ROW_NUMBER()
sql
SELECT
name,
score,
ROW_NUMBER() OVER(
ORDER BY score DESC
) AS rn
FROM student;
结果:
| name | score | rn |
|---|---|---|
| 张三 | 90 | 1 |
| 李四 | 90 | 2 |
| 王五 | 85 | 3 |
特点:
sql
即使分数相同,
排名也不会重复
2.RANK()
sql
SELECT
name,
score,
RANK() OVER(
ORDER BY score DESC
) AS rk
FROM student;
结果:
| name | score | rk |
|---|---|---|
| 张三 | 90 | 1 |
| 李四 | 90 | 1 |
| 王五 | 85 | 3 |
特点:
sql
相同分数排名一样,
但会跳名次
3.DENSE_RANK()
sql
SELECT
name,
score,
DENSE_RANK() OVER(
ORDER BY score DESC
) AS dr
FROM student;
结果:
| name | score | dr |
|---|---|---|
| 张三 | 90 | 1 |
| 李四 | 90 | 1 |
| 王五 | 85 | 2 |
特点:
sql
相同分数排名一样,
但不会跳名次
七、PARTITION BY(超级重要)
这个是窗口函数真正强大的地方。
你可以理解成:
"分组后,再在组内操作"
类似:
sql
GROUP BY
但:
sql
不会压缩数据
示例数据
| name | class | score |
|---|---|---|
| 张三 | 1班 | 90 |
| 李四 | 1班 | 85 |
| 王五 | 2班 | 95 |
| 赵六 | 2班 | 88 |
查询每个班的排名
sql
SELECT
name,
class,
score,
ROW_NUMBER() OVER(
PARTITION BY class
ORDER BY score DESC
) AS rn
FROM student;
结果:
| name | class | score | rn |
|---|---|---|---|
| 张三 | 1班 | 90 | 1 |
| 李四 | 1班 | 85 | 2 |
| 王五 | 2班 | 95 | 1 |
| 赵六 | 2班 | 88 | 2 |
八、SUM() 也能做窗口函数
这个很多人不知道。
查询累计工资
| month | salary |
|---|---|
| 1 | 1000 |
| 2 | 1500 |
| 3 | 2000 |
累计求和
sql
SELECT
month,
salary,
SUM(salary) OVER(
ORDER BY month
) AS total
FROM salary;
结果:
| month | salary | total |
|---|---|---|
| 1 | 1000 | 1000 |
| 2 | 1500 | 2500 |
| 3 | 2000 | 4500 |
九、窗口函数和 GROUP BY 的区别
这是核心。
GROUP BY
特点:
sql
会压缩数据
比如:
sql
SELECT class,
AVG(score)
FROM student
GROUP BY class;
最后:
sql
一组只剩一行
窗口函数
特点:
sql
保留原数据
比如:
sql
SELECT
name,
score,
AVG(score) OVER()
FROM student;
每个人的数据都还在。
十、实际开发中的经典场景
1.排行榜
sql
RANK()
ROW_NUMBER()
2.分页
sql
ROW_NUMBER()
3.TopN 查询
比如:
sql
每个班前3名
4.累计统计
sql
SUM() OVER()
5.环比分析
sql
LAG()
LEAD()
十一、LAG() 和 LEAD()
这个在数据分析里特别常见。
LAG()
取上一行数据。
sql
SELECT
month,
salary,
LAG(salary) OVER(
ORDER BY month
) AS prev_salary
FROM salary;
LEAD()
取下一行数据。
sql
SELECT
month,
salary,
LEAD(salary) OVER(
ORDER BY month
) AS next_salary
FROM salary;
十二、新手最容易懵的点
1.OVER() 必须写
窗口函数一定要配:
sql
OVER()
2.PARTITION BY 不是 GROUP BY
它只是:
sql
组内操作
不会压缩数据。
3.ORDER BY 非常重要
很多窗口函数:
sql
排名
累计
上一行
下一行
都依赖排序。
十三、最结
窗口函数本质:
在不减少原数据的情况下做统计。
最核心的几个函数
| 函数 | 作用 |
|---|---|
| ROW_NUMBER() | 行号 |
| RANK() | 排名(跳名次) |
| DENSE_RANK() | 排名(不跳名次) |
| SUM() OVER() | 累计求和 |
| LAG() | 上一行 |
| LEAD() | 下一行 |
最核心的语法
sql
函数名() OVER(
PARTITION BY 分组
ORDER BY 排序
)
sql
窗口函数 = 保留原数据 + 做统计