通过真实业务数据,练习产品分析 SQL。
SQL 课程 17:窗口函数:排名与累计值
GROUP BY 会把多行压缩成一行,但很多分析既想保留每笔明细,又想在旁边看到它在组内的排名或累计值。窗口函数正好解决这个问题:它会“看着一组行计算”,却不会把明细行折叠掉。
本课使用 RANK、DENSE_RANK、ROW_NUMBER 和 SUM() OVER。PARTITION BY 决定在哪个业务范围内计算,ORDER BY 决定排名或累计的先后顺序。
窗口函数里的排序是分析口径的一部分。做累计成交金额时,如果同一天有多笔商机,就需要用 opportunity_id 作为第二排序条件,保证每一步都能复现。
函数(...) OVER ( PARTITION BY 分组字段 ORDER BY 排序字段 ROWS BETWEEN ... AND ... ) AS 窗口指标
窗口函数与 GROUP BY 的区别
GROUP BY 的结果是一组一个汇总值,例如每位销售人员一行;窗口函数会把这个值写回组内的每条记录。例如一笔成交商机既可以保留自己的 close_value,也可以同时显示它在产品内的金额排名。
可以把窗口想成一扇移动的观察窗:PARTITION BY 划定窗户属于哪个组,ORDER BY 决定窗户从哪一行开始移动。
SELECT product, opportunity_id, close_value,
RANK() OVER (
PARTITION BY product
ORDER BY close_value DESC
) AS rank_in_product
FROM sales_pipeline
WHERE deal_stage = 'Won';小提醒:RANK 遇到并列值会跳号;DENSE_RANK 遇到并列值不跳号;ROW_NUMBER 即使金额相同也会给每行一个唯一序号。
排名函数:先选对含义,再选函数
RANK 适合回答“这笔商机在同产品中排第几”,并列第一后下一名会是第三。DENSE_RANK 适合连续名次。ROW_NUMBER 更适合去重或取每组前 N 条,因为每一行都有唯一编号。
三种常用排名函数
| 函数 | 并列时的表现 | 适合场景 |
|---|---|---|
| RANK() | 会跳号 | 竞赛式排名 |
| DENSE_RANK() | 不跳号 | 连续等级排名 |
| ROW_NUMBER() | 每行唯一序号 | 去重、取每组前 N 条 |
累计值需要明确窗口范围
SUM(close_value) OVER (PARTITION BY sales_agent ORDER BY close_date, opportunity_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 会从每位销售人员的第一笔成交开始,逐笔累加到当前记录。
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 的意思是“从本组第一行到当前行”。如果没有稳定的 ORDER BY,累计值的每一步就可能因为同一天记录的先后不确定而难以复核。
练习
窗口题会保留明细行,并严格核对窗口计算结果和最终排序。请不要把窗口函数误写成会压缩行数的 GROUP BY。