摘要: 还在用几十行子查询做排名?还在为“累计求和”写复杂的自连接?那是你还没掌握 SQL 的核武器——开窗函数。本文带你从原理到实战,彻底搞懂它!
对比图
做数据分析或者后端开发的兄弟们,你们有没有遇到过这种抓狂的场景:
客户让你出一张报表,要求:
- 列出每个员工的姓名和薪水;
- 同时列出该员工所在部门的平均薪水;
- 还要列出该员工在部门内的薪水排名。
如果你不会开窗函数,你可能得写两个 GROUP BY 的子查询,然后再 JOIN 回原表。代码写出来又长又臭,跑起来慢得像蜗牛。
今天,咱们就来聊聊 SQL 界的“瑞士军刀”——开窗函数(Window Functions)。
一、 为什么要有开窗函数?解决什么痛点?
在开窗函数诞生之前,SQL 的世界里有一个巨大的矛盾:
- 明细查询(SELECt ): 能看到每一行数据,但看不了统计信息(如总和、平均值)。
- 聚合查询(GROUP BY): 能看到统计信息,但每一行的明细被“压扁”了,丢失了细节。
痛点非常明显:
我想在保留每一行明细数据的同时,还能在旁边“开一扇窗”,看到整体或分组的统计数据。
开窗函数就是为了解决这个问题而生的! 它允许你在不减少原表行数的情况下,对数据进行分组计算。
二、 核心语法:一图看懂 ️
开窗函数的长相通常是这样的:
SQL
<函数名> OVER ( PARTITION BY <分组字段> ORDER BY <排序字段> [ROWS/RANGE BETWEEN ...])- PARTITION BY:把你理解为 GROUP BY,但它不会折叠行,只是把数据在逻辑上切成几块。
- ORDER BY:决定了窗户里的数据是怎么排列的(主要用于排名和累计计算)。
三、 全场景示例:建议收藏
为了演示,假设我们有一张 Orders (订单表),字段有:User_ID(用户), Order_Date(日期), Amount(金额)。
1. 排名神器:ROW_NUMBER, RANK, DENSE_RANK
场景: 找出每个用户金额最高的订单,或者给订单排名。
SQL
SELECT User_ID, Amount, -- 1. 这种排名不并列,序号连续(1,2,3,4) ROW_NUMBER() OVER (PARTITION BY User_ID ORDER BY Amount DESC) as rn, -- 2. 这种排名有并列,序号跳跃(1,1,3,4) RANK() OVER (PARTITION BY User_ID ORDER BY Amount DESC) as rk, -- 3. 这种排名有并列,序号连续(1,1,2,3)-> 最推荐! DENSE_RANK() OVER (PARTITION BY User_ID ORDER BY Amount DESC) as drFROM Orders;图解区别: 考了 100 分的两个人,RANK 排名是第 1 和第 1,第三个人就是第 3 名;而 DENSE_RANK 第三个人是第 2 名。
2. 聚合神器:累计求和与移动平均
场景: 想要看截至当天的“累计销售额”(Running Total),或者“过去3天的平均值”。
SQL
SELECt Order_Date, Amount, -- 累计求和:从第一天加到当前行 SUM(Amount) OVER (ORDER BY Order_Date) as Running_Total, -- 移动平均:计算当前行和前两行的平均值 AVG(Amount) OVER (ORDER BY Order_Date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as Moving_AvgFROM Orders;痛点解决: 以前做“累计求和”要写自连接(Self-Join),性能极差。用开窗函数,一行代码搞定,性能提升 10 倍不止。
3. 偏移神器:LAG 和 LEAD
场景: 计算“环比增长率”(和昨天比)、“同比增长率”。需要把上一行的数据拿下来和当前行计算。
SQL
SELECt Order_Date, Amount, -- LAG(字段, N): 往前取第 N 行的数据 LAG(Amount, 1) OVER (ORDER BY Order_Date) as Last_Order_Amount, -- LEAD(字段, N): 往后取第 N 行的数据 LEAD(Amount, 1) OVER (ORDER BY Order_Date) as Next_Order_AmountFROM Orders;妙用: 计算 (Amount - Last_Order_Amount) / Last_Order_Amount 就是环比增长率!再也不用复杂的 JOIN 了。
四、 总结:开窗函数的成效是什么?
- 极简代码: 原本 50 行的逻辑,现在 5 行搞定。逻辑清晰,维护方便。
- 性能飞跃: 数据库引擎对开窗函数有专门优化,避免了大量的子查询和笛卡尔积,计算速度极快。
- 解决复杂逻辑: 轻松处理“Top N 问题”、“连续登录问题”、“累计报表问题”。
SQL 是数据人的基本功,而开窗函数是区分“小白”和“高手”的分水岭。如果你还在用 Excel 处理百万行数据,或者在代码里写死循环处理数据,赶紧试试 SQL 开窗函数吧!
如果你觉得这篇文章有帮助,欢迎点赞、收藏和分享,我们一起学习,共同进步!
