MySQL窗口函数的使用总结

本文深入介绍了MySQL从8.0版本开始引入的窗口函数,包括其使用场景、基本语法、与GROUP BY的区别以及功能。通过示例展示了如何在组内排名、TopN问题中应用窗口函数,并对比了使用窗口函数与普通方法的优劣。同时,提供了用户登录记录查询的实战案例,解释了RANK(), DENSE_RANK()和ROW_NUMBER()函数的差异,并给出了两种实现方法。

1.版本介绍:mysql的窗口函数从8.0版本开始使用,之前的版本都不支持窗口函数

2.使用场景:需要组内排名时使用

        例如:

        a.排名问题:每个部门按业绩来排名

        b.topN问题:找出不同部门排名前N的员工进行奖励

3.窗口函数使用的基本语法:

        <窗口函数> over (partition by <用于分组的列名>order by <用于排序的列名>)

        1). 专用窗口函数:包括后面要讲到的 rank, dense_rank, row_number等专用窗口函数;

        2).聚合函数:如sum. avg, count, max, min等

4.partition by和group by区别:

        1). partition by用来对表分组;

        2).order by子句的功能是对分组后的结果进行排序(组内排序),默认是按照升序(asc)排列,(desc)降序排列;

        3).group by :分组后会改变行数;partition by:分组汇总行数不变。

5.窗口函数的功能:

        1)同时具有分组和排序的功能

        2)不减少原表的行数

6.窗口函数使用示例:

select *,
   rank() over (order by 成绩 desc) as ranking,
   dense_rank() over (order by 成绩 desc) as dese_rank,
   row_number() over (order by 成绩 desc) as row_num
from 班级表

7.示例结果展示:

 8.总结:

rank函数:会根据分组字段相同的数据排名,相同数据排名相同,不同数据会自动顺延排名数;

dense函数:会根据字段相同的数据排名,相同数据排名相同,不同数据依次递增排名;

row_number函数:根据字段数据的行数依次递增排名,不受数据的影响

9.案例分析:

create table userlog 
(
    id int ,
    name varchar(10),
    EmailAddress varchar(50),
    lastlogon varchar(50)
);
insert into userlog values(100,'test4','test4@yahoo.cn','2007-11-25 16:31:26');
insert into userlog values(13,'test1','test4@yahoo.cn','2007-3-22 16:27:07');
insert into userlog values(19,'test1','test4@yahoo.cn','2007-10-25 14:13:46');
insert into userlog values(42,'test1','test4@yahoo.cn','2007-10-25 14:20:10');
insert into userlog values(45,'test2','test4@yahoo.cn','2007-4-25 14:17:39');
insert into userlog values(49,'test2','test4@yahoo.cn','2007-5-25 14:22:36');

-- 1.查询出每个用户最近一次登录的记录(每个用户只显示一条最近登录的记录)及给出每个用户的登录总次数(同一天多次登录认为是一次)
select `name`,EmailAddress,date_format(lastlogon,'%Y-%m-%d %H:%m:%s') time1,date_format(substring_index(lastlogon,' ',1),'%Y-%m-%d') day1 from userlog;

select a.`name`,max(a.time1) 最近登录详细时间,max(a.day1) 最近登陆时间,count(distinct a.day1) 总次数 
from (select `name`,EmailAddress,date_format(lastlogon,'%Y-%m-%d %H:%m:%s') time1,date_format(substring_index(lastlogon,' ',1),'%Y-%m-%d') day1 from userlog)a 
group by a.`name`;


-- 2.生成一张临时表,表名自定,四列数据,分别提是:Name, Last Logon,Num_logontime(要求:按时间给出每个人的登录次数,登录时间最早的为1,之后分别为2, 3,4等), Num_logonday(要求:按天给出每个人的登录次数,同一天多次登录认为是同一次,最早的一次标记为1,之后的依次类推)

select id,`name`, date_format(lastlogon,'%Y-%m-%d %H:%m:%s') time2 ,date_format(substring_index(lastlogon,' ',1),'%Y-%m-%d') day2,count(id) 次数 from userlog group by day2;


# 方法一:窗口函数添加排序列
select b.`name`,b.time2,
row_number() over(partition by b.`name` order by b.day2 ) Num_logontime,
rank() over(partition by b.`name` order by b.day2 ) Num_logonday,
dense_rank() over(partition by b.`name` order by b.day2 ) dense_logonday
from (select `name`, date_format(lastlogon,'%Y-%m-%d %H:%m:%s') time2 ,date_format(substring_index(lastlogon,' ',1),'%Y-%m-%d') day2 from userlog)b;


# 方法二:创建变量添加辅助列添加排序列
DROP TABLE IF EXISTS tmp_table;
CREATE TEMPORARY TABLE tmp_table 
select 
user2.name as Name ,user2.time1 as lastlogon ,user2.rank1 as num_logontime,user2.rank2 as num_logonday
from 
    (select  *  ,
        @rank:=if(@nam=user1.name,@rank+1,1) rank1,
        @rank1:=if(@nam=user1.name,if(@da=user1.day1,@rank1,@rank1+1),1) rank2,
        @nam:=user1.name,
        @da:=user1.day1
        from (select name,EmailAddress,date_format(lastlogon, '%Y-%m-%d %H:%i:%s') as time1,date_format(substring_index(lastlogon,' ',1),'%Y-%m-%d') as day1 from userlog order by name asc  ,time1 asc)as user1 ,(select @rank:=0,@rank1:=0,@nam:=null,@da:=null) temp order by user1.name,user1.time1) as user2;select * from tmp_table ;

案例总结:通过上述mysql查询案例,明显发现,使用窗口函数比普通添加辅助列的方法更容易理解、更容易添加辅助列,代码量少,不易出错。

<think>我们被要求回答关于MySQL窗口函数实际应用场景的问题。根据提供的引用,我们可以总结出几个典型的应用场景。 引用[1]提到:数据排名、累计计算、趋势分析、同比环比计算。 引用[4]提到:计算累计值、比较历史数据的变化情况、分类排名。 引用[5]给出了一个具体例子:找出每个部门薪资最高的3名员工(使用ROW_NUMBER()进行排名并筛选)。 因此,我们可以整理出常见的应用场景如下: 1. 数据排名:在每个分组内进行排序,如ROW_NUMBER(), RANK(), DENSE_RANK()等。 - 例如:每个部门按薪资排名,销售排名等。 2. 累计计算:计算累计和、累计平均等。 - 例如:计算每个月的累计销售额,累计注册用户数等。 3. 移动平均/移动求和:通过定义窗口框架(Window Frame)来计算一定范围内的移动平均值或和。 - 例如:计算近3个月的平均销售额。 4. 同比环比计算:与历史同期数据的比较(同比)或与上一期数据的比较(环比)。 - 例如:计算本月销售额与上月销售额的比率(环比),或者与去年同月销售额的比率(同比)。 5. 分组内取特定条件的数据:如取每个部门的前N名,或者取每个班级的前三名等。 6. 计算分组内的占比:比如计算每个员工的销售额占部门总销售额的比例。 7. 访问相邻行的数据:使用LAG()或LEAD()函数访问当前行的前一行或后一行的数据。 - 例如:计算每个用户的上次登录时间与本次登录时间的间隔。 下面我们通过一些具体的SQL示例来展示这些应用场景: ### 1. 数据排名(每个部门薪资排名) ```sql SELECT name, department, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num, RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_num, DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank_num FROM employees; ``` ### 2. 累计计算(每个月的累计销售额) 假设有一张销售表`sales`,包含月份`month`和销售额`amount`。 ```sql SELECT month, amount, SUM(amount) OVER (ORDER BY month) AS cumulative_amount FROM sales; ``` ### 3. 移动平均(近3个月的移动平均销售额) 同样使用`sales`表: ```sql SELECT month, amount, AVG(amount) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales; ``` ### 4. 同比环比 假设有月销售额表`sales_monthly`,包含年份`year`,月份`month`,销售额`amount`。 环比计算(当前月与上月比较): ```sql SELECT year, month, amount, LAG(amount) OVER (ORDER BY year, month) AS prev_month_amount, (amount - LAG(amount) OVER (ORDER BY year, month)) / LAG(amount) OVER (ORDER BY year, month) AS month_over_month FROM sales_monthly; ``` 同比计算(与去年同月比较): ```sql SELECT year, month, amount, LAG(amount, 12) OVER (ORDER BY year, month) AS prev_year_amount, (amount - LAG(amount, 12) OVER (ORDER BY year, month)) / LAG(amount, 12) OVER (ORDER BY year, month) AS year_over_year FROM sales_monthly; ``` ### 5. 分组内取前N名(如每个部门薪资前三名) 引用[5]中的示例: ```sql SELECT * FROM ( SELECT name, department, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank FROM employees ) ranked WHERE salary_rank <= 3 ORDER BY department, salary_rank; ``` ### 6. 计算分组内占比(每个员工销售额占部门总销售额的比例) 假设有销售表`sales_employees`,包含员工姓名`name`,部门`department`,销售额`sales`。 ```sql SELECT name, department, sales, sales / SUM(sales) OVER (PARTITION BY department) AS sales_percentage FROM sales_employees; ``` ### 7. 访问相邻行数据(计算用户连续登录时间差) 假设有用户登录表`user_logins`,包含用户ID`user_id`,登录时间`login_time`。 ```sql SELECT user_id, login_time, LAG(login_time) OVER (PARTITION BY user_id ORDER BY login_time) AS last_login_time, TIMESTAMPDIFF(DAY, LAG(login_time) OVER (PARTITION BY user_id ORDER BY login_time), login_time) AS days_since_last_login FROM user_logins; ``` 这些示例涵盖了MySQL窗口函数的主要应用场景。在实际业务中,窗口函数可以极大地简化复杂的数据分析查询,避免使用复杂的自连接或子查询,提高查询效率和可读性。 引用[3]特别提到了窗口框架(Window Frame)的高级用法,它允许我们在窗口内定义一个更精确的范围。例如,在移动平均的例子中,我们使用了`ROWS BETWEEN 2 PRECEDING AND CURRENT ROW`来指定当前行及其前两行作为窗口范围。 综上所述,MySQL窗口函数在处理排名、累计计算、移动平均、同比环比、分组内筛选、占比计算和相邻数据比较等场景中非常有用。在实际应用中,需要根据业务需求选择合适的窗口函数和窗口定义。 [^1]: MySQL 窗口函数为数据处理和分析提供了强大而灵活的工具,通过丰富的函数类型和灵活的窗口定义,能够轻松应对各种复杂的计算需求。无论是数据排名、累计计算,还是趋势分析、同比环比计算,窗口函数都能发挥重要作用。 [^3]: 窗口框架(Window Frame)是 SQL 窗口函数中一个高级但极其有用的功能,它允许你更精细地控制计算范围,而不仅仅是按 PARTITION BY 分组或按 ORDER BY 排序。 [^5]: MySQL窗口函数详解。使用ROW_NUMBER()进行部门内薪资排名并筛选前三名。 §§相关问题§§ 1. 窗口函数与普通聚合函数有什么区别? 2.使用窗口函数时,如何优化查询性能? 3. 窗口函数在哪些版本的MySQL中可用? 4. 能否举例说明窗口函数在电商数据分析中的具体应用? </think>### MySQL窗口函数的实际应用场景 MySQL窗口函数(Window Functions)是数据分析中的强大工具,特别适用于需要在保留原始行数据的同时进行分组计算、排名或跨行分析的场景。以下是其核心应用场景及具体示例: #### 1. **数据排名与分组筛选** **场景**:在每组内排序并筛选Top N记录(如部门薪资前三名) **示例**(引用[5]): ```sql SELECT * FROM ( SELECT name, department, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank FROM employees ) ranked WHERE salary_rank <= 3 ORDER BY department, salary_rank; ``` - 使用 `ROW_NUMBER()` 分区排序,筛选部门薪资前三名员工[^5]。 #### 2. **累计计算(Running Total)** **场景**:计算随时间增长的累计值(如月度累计销售额) **示例**: ```sql SELECT order_date, daily_sales, SUM(daily_sales) OVER (ORDER BY order_date) AS cumulative_sales FROM sales; ``` - `SUM() OVER` 按时间顺序累加每日销售额,形成累计值[^1]。 #### 3. **同比/环比分析** **场景**:比较当前值与历史同期(同比)或前一期(环比) **示例**(环比计算): ```sql SELECT month, revenue, revenue - LAG(revenue) OVER (ORDER BY month) AS month_over_month_growth FROM monthly_revenue; ``` - `LAG()` 访问上一行数据,计算月度增长率[^4]。 #### 4. **移动平均(Moving Average)** **场景**:平滑数据波动(如股票价格近7日均线) **示例**(引用[3]): ```sql SELECT date, closing_price, AVG(closing_price) OVER ( ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) AS 7_day_avg FROM stock_prices; ``` - 窗口框架 `ROWS BETWEEN` 定义7行范围计算移动平均[^3]。 #### 5. **分组占比分析** **场景**:计算个体在组内的贡献度(如员工占部门薪资比例) **示例**: ```sql SELECT name, department, salary, salary / SUM(salary) OVER (PARTITION BY department) AS salary_ratio FROM employees; ``` - 分区内计算薪资占比[^1]。 #### 6. **数据断层处理(Gap Analysis)** **场景**:识别序列中的缺失值(如连续登录中断的用户) **示例**: ```sql SELECT user_id, login_date, login_date - LAG(login_date) OVER (PARTITION BY user_id ORDER BY login_date) AS gap_days FROM logins; ``` - `LAG()` 检测相邻登录日期间隔,定位异常中断[^4]。 #### 7. **趋势分析(Trend Calculation)** **场景**:计算滚动统计量(如近30天留存率) **示例**: ```sql SELECT date, AVG(retention_rate) OVER ( ORDER BY date RANGE BETWEEN INTERVAL 29 DAY PRECEDING AND CURRENT ROW ) AS 30d_avg_retention FROM retention_metrics; ``` #### **典型业务场景** - **财务报表**:月度累计营收、年度同比 - **用户行为分析**:连续签到天数、活跃度排名 - **库存管理**:滚动平均销量预测 - **运营监控**:实时排名(如直播间打赏榜) > 关键优势:**避免自连接/子查询**,简化复杂逻辑,提升性能[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值