做排行榜和环比还在用子查询?SQL 窗口函数一次搞定

举报
茉莉风铃 发表于 2026/09/10 10:55:16 2026/09/10
【摘要】 排名、环比、累计这些分析需求,用子查询嵌套又慢又难读。这篇用 SQL 窗口函数(ROW_NUMBER / LAG / SUM OVER)一次性解决月度销售排名、环比增长和部门累计,含可运行的示例 SQL 和对比。

做报表时常遇到三类需求:每人每月业绩排第几、这个月比上个月涨了多少、部门累计销售额到哪了。新手爱用子查询嵌套,写出来又长又慢还容易错。其实 SQL 的窗口函数(Window Function)就是为这种"分组内排名、行间比较"而生的——它不聚合、保留每一行,一行 SQL 全解决。

一、示例表

一张简单的销售表:

CREATE TABLE sales (
  emp    VARCHAR(20),
  dept   VARCHAR(20),
  month  DATE,
  amount INT
);
-- 示例数据
INSERT INTO sales VALUES
  ('张三','销售一部','2024-01-01',8000),
  ('李四','销售一部','2024-01-01',9500),
  ('张三','销售一部','2024-02-01',9000),
  ('李四','销售一部','2024-02-01',7000);

二、组内排名:ROW_NUMBER / RANK

要"每个部门内按业绩排名",窗口函数一行搞定:

SELECT emp, dept, month, amount,
       ROW_NUMBER() OVER (
         PARTITION BY dept
         ORDER BY amount DESC
       ) AS rn
FROM sales;

PARTITION BY dept 按部门分组(组内独立排名),ORDER BY amount DESC 决定排名顺序。关键点:窗口函数不合并行,原表几行结果就几行,只是多了一列排名。想要并列同名次用 RANK(),并列后跳号用 DENSE_RANK()

三、环比增长:LAG 取上一期

“这个月比上个月涨了多少”,用 LAG 取同一人上一期的金额:

SELECT emp, month, amount,
       LAG(amount) OVER (
         PARTITION BY emp
         ORDER BY month
       ) AS prev_amount,
       ROUND(
         (amount - LAG(amount) OVER (PARTITION BY emp ORDER BY month))
         / LAG(amount) OVER (PARTITION BY emp ORDER BY month) * 100, 1
       ) AS growth_pct
FROM sales
ORDER BY emp, month;

LAG(amount) 默认取"排序后前一行",配合 PARTITION BY emp 保证只跟自己上一期比,不会串到别人头上。

四、累计求和:SUM OVER

“部门累计销售额”,用 SUM() OVER 做累计:

SELECT emp, month, amount,
       SUM(amount) OVER (
         PARTITION BY dept
         ORDER BY month
         ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
       ) AS dept_running_total
FROM sales;

ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 表示"从组内第一行到当前行"累计。不写这个范围默认就是累计到当前行。

五、要注意的几个关键点

  • GROUP BY 和窗口函数混淆:窗口函数不能和 GROUP BY 混着当聚合用,它保留明细行。要既聚合又排名,先聚合出结果再套一层窗口函数。
  • ORDER BY 缺了排名就乱ROW_NUMBER() 必须配合 ORDER BY,否则排名顺序是未定义的。
  • LAG 第一行是 NULL:首期没有"上一期",prev_amount 为 NULL,算环比前先 COALESCE 兜底。
  • 性能:窗口函数比多层子查询快,但 PARTITION BY 的列该加索引还是得加,数据量大时差别明显。
需求 函数 说明
组内排名 ROW_NUMBER/RANK 不聚合、保留每行
环比/同比 LAG / LEAD 取前/后行
累计求和 SUM OVER 指定累计范围

image.png

小结

窗口函数把"排名、环比、累计"这类分析从"多层子查询地狱"里救了出来:一句 OVER (PARTITION BY ... ORDER BY ...) 同时搞定分组和排序,还不丢掉明细行。记住三件套——ROW_NUMBER 排名、LAG 取上期、SUM OVER 累计——日常报表八成以上的分析需求都能直接覆盖。比子查询可读、比应用层算得快,属于"早用早舒服"的 SQL 特性。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。