明星八卦
开窗函数(拒绝写“屎山”代码!一篇讲透SQL开窗函数,数据分析师必备)

摘要: 还在用几十行子查询做排名?还在为“累计求和”写复杂的自连接?那是你还没掌握 SQL 的核武器——开窗函数。本文带你从原理到实战,彻底搞懂它!

拒绝写“屎山”代码!一篇讲透SQL开窗函数,数据分析师必备nerror="javascript:errorimg.call(this);">

对比图


做数据分析或者后端开发的兄弟们,你们有没有遇到过这种抓狂的场景:

客户让你出一张报表,要求:

  1. 列出每个员工的姓名和薪水;
  2. 同时列出该员工所在部门的平均薪水;
  3. 还要列出该员工在部门内的薪水排名。

如果你不会开窗函数,你可能得写两个 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 了。

四、 总结:开窗函数的成效是什么?

  1. 极简代码: 原本 50 行的逻辑,现在 5 行搞定。逻辑清晰,维护方便。
  2. 性能飞跃: 数据库引擎对开窗函数有专门优化,避免了大量的子查询和笛卡尔积,计算速度极快。
  3. 解决复杂逻辑: 轻松处理“Top N 问题”、“连续登录问题”、“累计报表问题”。

SQL 是数据人的基本功,而开窗函数是区分“小白”和“高手”的分水岭。如果你还在用 Excel 处理百万行数据,或者在代码里写死循环处理数据,赶紧试试 SQL 开窗函数吧!

如果你觉得这篇文章有帮助,欢迎点赞、收藏和分享,我们一起学习,共同进步!


顶一下()     踩一下()

热门推荐

发表评论
0评