首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >MySQL窗口函数实战:用ROW_NUMBER()和SUM() OVER()解决复杂统计案例

MySQL窗口函数实战:用ROW_NUMBER()和SUM() OVER()解决复杂统计案例

原创
作者头像
小明互联网技术分享社区
发布2025-07-11 09:09:11
发布2025-07-11 09:09:11
1.5K3
举报
文章被收录于专栏:MYSQLMYSQL

一、当SQL遇上窗口函数

"为什么同样的统计需求,别人用3行SQL就能实现,我却要写30行嵌套查询?" 这是许多SQL开发者都曾有过的困惑。在后端程序员日常工作中,排名统计、累计计算、分组对比等需求就像顽固的"钉子户",直到我们遇见MySQL 8.0带来的窗口函数——这个数据库领域的"瑞士军刀",彻底改变了我们处理复杂统计的方式。

今天小编通过三个真实业务场景,手把手教大家用ROW_NUMBER()和SUM() OVER()两大神器,轻松攻克那些曾让你头疼的数据库统计难题!

二、窗口函数基础:数据分析的透视镜

2.1 什么是窗口函数?

窗口函数(Window Function)是一种特殊类型的SQL函数,它能够在保持原有行数据完整性的同时,对数据集的特定子集(称为"窗口")进行计算。就像给数据安装了一面"透视镜",既能看到整体数据全貌,又能聚焦局部细节。

2.2 与普通聚合函数的区别

<!--br {mso-data-placement:same-cell;}--> td {white-space:nowrap;border:0.5pt solid #dee0e3;font-size:10pt;font-style:normal;font-weight:normal;vertical-align:middle;word-break:normal;word-wrap:normal;}

特征

普通聚合函数

窗口函数

数据维度

折叠为单行

保留原始行数据

计算范围

全局或GROUP BY分组

可定义滑动窗口范围

典型函数

SUM/AVG/COUNT等

ROW_NUMBER/RANK/LEAD等

使用场景

汇总统计

复杂分析(排名/移动平均等)

2.3 核心语法结构

代码语言:javascript
复制
函数名() OVER (
    [PARTITION BY 分区字段] 
    [ORDER BY 排序字段]
    [ROWS/RANGE 窗口范围]
)
  • PARTITION BY:将数据划分为多个窗口(类似GROUP BY)
  • ORDER BY:决定窗口内的数据排序方式
  • ROWS/RANGE:定义窗口范围(前N行/后N行/当前行等)

2.4 常用窗口函数清单

<!--br {mso-data-placement:same-cell;}--> td {white-space:nowrap;border:0.5pt solid #dee0e3;font-size:10pt;font-style:normal;font-weight:normal;vertical-align:middle;word-break:normal;word-wrap:normal;}

函数类型

代表函数

典型应用

排名函数

ROW_NUMBER/RANK/DENSE_RANK

销售排名/成绩排名

聚合函数

SUM/AVG/COUNT OVER()

累计值/移动平均

分布函数

CUME_DIST/PERCENT_RANK

数据分布分析

前后函数

LAG/LEAD

环比增长/上期对比

2.5 MySQL版本要求

  • MySQL 8.0+ 原生支持窗口函数
  • MySQL 5.7及以下版本可通过变通方法模拟部分功能
  • 执行 SELECT VERSION(); 查看数据库版本

三、ROW_NUMBER():排名统计的终极方案

场景1:销售团队业绩龙虎榜

代码语言:javascript
复制
-- 创建示例表
CREATE TABLE sales (
    sales_id INT PRIMARY KEY,
    salesperson VARCHAR(20),
    region VARCHAR(10),
    amount DECIMAL(10,2),
    sale_date DATE
);

-- 插入测试数据
INSERT INTO sales VALUES
(1, '张三', '华东', 15800, '2023-06-01'),
(2, '李四', '华南', 24500, '2023-06-02'),
(3, '王五', '华东', 18200, '2023-06-03'),
(4, '赵六', '华北', 31500, '2023-06-04'),
(5, '魏七', '华东', 22700, '2023-06-05');

-- 按地区分区排名
SELECT 
    salesperson,
    region,
    amount,
    ROW_NUMBER() OVER (PARTITION BY region ORDER BY amount DESC) AS region_rank
FROM sales;

执行结果:

salesperson

region

amount

region_rank

赵六

华北

31500.00

1

李四

华南

24500.00

1

张三

华东

22700.00

1

王五

华东

18200.00

2

魏七

华东

15800.00

3

技巧延伸:

  • 分页查询优化:WHERE region_rank BETWEEN 6 AND 10实现高效分页
  • 去重处理:通过ROW_NUMBER() = 1筛选最新记录
  • TOP N统计:结合CTE公用表达式进行多层级排名

二、SUM() OVER():累计统计的魔法棒

场景2:电商平台GMV趋势分析

代码语言:javascript
复制
-- 创建订单表
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    user_id INT,
    order_amount DECIMAL(10,2),
    order_date DATE
);

-- 插入测试数据
INSERT INTO orders VALUES
(1, 101, 1500, '2023-06-01'),
(2, 102, 2300, '2023-06-01'),
(3, 101, 800, '2023-06-02'),
(4, 103, 4500, '2023-06-03'),
(5, 102, 1200, '2023-06-03');

-- 按日期累计统计
SELECT 
    order_date,
    SUM(order_amount) OVER (ORDER BY order_date) AS running_total,
    SUM(order_amount) OVER (ORDER BY order_date 
                         ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg_3d
FROM orders
GROUP BY order_date;

执行结果:

order_date

running_total

moving_avg_3d

2023-06-01

3800.00

3800.00

2023-06-02

4600.00

4600.00

2023-06-03

10300.00

7600.00

高级用法:

  • 移动平均线:ROWS BETWEEN 6 PRECEDING AND CURRENT ROW实现7日均线
  • 同期对比:SUM() OVER (PARTITION BY MONTH(order_date))按月分区
  • 累计占比:结合LAG函数计算增长率

三、组合技:窗口函数综合实战

场景3:人力资源薪酬分析

代码语言:javascript
复制
-- 创建员工表
CREATE TABLE employees (
    emp_id INT PRIMARY KEY,
    name VARCHAR(20),
    department VARCHAR(20),
    salary DECIMAL(10,2),
    hire_date DATE
);

-- 插入测试数据
INSERT INTO employees VALUES
(1, '陈强', '技术部', 25000, '2020-03-15'),
(2, '李娜', '市场部', 18000, '2021-07-22'),
(3, '王伟', '技术部', 32000, '2019-11-05'),
(4, '张敏', '财务部', 21000, '2022-02-18'),
(5, '赵刚', '市场部', 19500, '2020-09-30');

-- 多维度分析
SELECT 
    department,
    name,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS dept_rank,
    SUM(salary) OVER (PARTITION BY department) AS dept_total,
    salary/SUM(salary) OVER (PARTITION BY department) AS salary_ratio
FROM employees;

执行结果:

department

name

salary

dept_rank

dept_total

salary_ratio

技术部

王伟

32000.00

1

57000.00

0.5614

技术部

陈强

25000.00

2

57000.00

0.4386

市场部

赵刚

19500.00

1

37500.00

0.5200

市场部

李娜

18000.00

2

37500.00

0.4800

财务部

张敏

21000.00

1

21000.00

1.0000

业务价值:

  1. 快速识别部门薪资TOP N员工
  2. 直观查看部门人力成本构成
  3. 计算员工薪资在部门中的占比
  4. 为薪酬调整提供数据支持

四、避坑指南与性能优化

常见问题解决方案:

  1. 重复排名问题:改用DENSE_RANK()或RANK()
    代码语言:javascript
    复制
    SELECT 
        salary,
        ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num,
        RANK() OVER (ORDER BY salary DESC) AS rank,
        DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
    FROM employees;
  2. 动态窗口设置:使用RANGE模式处理日期区间
    代码语言:javascript
    复制
    SUM(sales) OVER (ORDER BY sale_date 
                    RANGE BETWEEN INTERVAL 6 DAY PRECEDING AND CURRENT ROW)
  3. 性能优化技巧
    1. 优先使用ROWS子句指定物理行
    2. 在PARTITION BY字段上建立索引
    3. 避免在窗口函数中使用复杂表达式

总结

通过以上内容,相信大家已经感受到窗口函数在数据分析中的强大威力,大大降低了SQL编写的难度。大家是否有其他更好的使用场景欢迎评论区沟通交流!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、当SQL遇上窗口函数
  • 二、窗口函数基础:数据分析的透视镜
    • 2.1 什么是窗口函数?
    • 2.2 与普通聚合函数的区别
    • 2.3 核心语法结构
    • 2.4 常用窗口函数清单
    • 2.5 MySQL版本要求
  • 三、ROW_NUMBER():排名统计的终极方案
    • 场景1:销售团队业绩龙虎榜
  • 二、SUM() OVER():累计统计的魔法棒
    • 场景2:电商平台GMV趋势分析
  • 三、组合技:窗口函数综合实战
    • 场景3:人力资源薪酬分析
  • 四、避坑指南与性能优化
    • 常见问题解决方案:
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档