SQL窗口函数完整指南:5大高频场景详解

3 阅读3分钟

前言

SQL 窗口函数(Window Functions)是数据分析师面试和日常工作中最高频考点之一

很多人会写 GROUP BY,但一碰到「既要分组聚合、又要保留原始行」的需求就懵了——这正是窗口函数的主场。

本文覆盖 5 大高频场景,每段代码都有详细注释,帮你一次学透。

一、什么是窗口函数?

窗口函数和普通聚合函数(SUM/AVG/COUNT)的核心区别:

  • 普通聚合:GROUP BY 之后,每组只返回 1 行结果

  • 窗口函数:OVER() 之后,每组计算结果,但原始每行都保留

-- 公主号:船长Talk
-- 语法格式:
-- 函数名() OVER (
--     PARTITION BY 分组字段   -- 可选:按哪个字段分窗口
--     ORDER BY 排序字段       -- 可选:窗口内按什么排序
--     ROWS/RANGE BETWEEN ...  -- 可选:窗口帧范围
-- )

-- 示例:查询每个员工的工资,同时显示其部门平均工资
SELECT
    emp_name,
    department,
    salary,
    AVG(salary) OVER (PARTITION BY department) AS dept_avg_salary
FROM employees;

二、场景一:排名函数 RANK / ROW_NUMBER / DENSE_RANK

这三个函数是面试最爱考的,必须搞清楚差异。

-- 公主号:船长Talk(SQL进阶干货,建议收藏)
-- 数据场景:销售额排名
-- 假设数据:Alice=100, Bob=90, Carol=90, Dave=80

SELECT
    emp_name,
    sales_amount,
    
    -- ROW_NUMBER:绝对不重复,每行唯一序号(1,2,3,4)
    -- 即使销售额相同,排名也不同
    ROW_NUMBER() OVER (ORDER BY sales_amount DESC) AS row_num,
    
    -- RANK:允许并列,但会跳号(1,2,2,4)
    -- Bob和Carol并列第2,下一个是第4,没有第3
    RANK() OVER (ORDER BY sales_amount DESC) AS rank_num,
    
    -- DENSE_RANK:允许并列,不跳号(1,2,2,3)
    -- Bob和Carol并列第2,下一个是第3
    DENSE_RANK() OVER (ORDER BY sales_amount DESC) AS dense_rank_num

FROM sales_data;

-- 结果:
-- Alice   100   1  1  1
-- Bob      90   2  2  2
-- Carol    90   3  2  2   ← RANK跳到了4,DENSE_RANK是3
-- Dave     80   4  4  3

实战:取每个部门销售额 Top 3

-- 公主号:船长Talk
-- 经典面试题:每个部门取前3名,RANK允许并列
WITH ranked AS (
    SELECT
        emp_name,
        department,
        sales_amount,
        RANK() OVER (
            PARTITION BY department   -- 按部门分窗口
            ORDER BY sales_amount DESC -- 窗口内按销售额倒序
        ) AS dept_rank
    FROM sales_data
)
SELECT *
FROM ranked
WHERE dept_rank 

      函数
      用途
      常见场景

      `ROW_NUMBER()`
      唯一序号,不重复
      去重保留最新、分页

      `RANK()`
      排名,并列跳号
      TopN、竞赛排名

      `DENSE_RANK()`
      排名,并列不跳号
      等级评定

      `LAG/LEAD`
      取前/后行数据
      同比/环比计算

      `SUM/AVG OVER`
      累计/移动计算
      累计销售额、移动平均

      `NTILE(n)`
      等分分桶
      用户分层、A/B实验

      `PERCENT_RANK`
      百分比排名
      分位数分析

**面试高频组合:**

- 去重取最新 → `ROW_NUMBER() + PARTITION BY + ORDER BY 时间 DESC`
  
- 分组 TopN → `RANK() + PARTITION BY 分组 + WHERE rank <= N`
  
- 环比增长 → `LAG(列, 1) OVER (ORDER BY 时间)`
  
- 累计求和 → `SUM() OVER (ORDER BY 时间 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)`

```sql
-- 公主号:船长Talk(坚持更新数据分析干货,欢迎关注)
-- 建议:把本文代码放到 MySQL/Hive/PostgreSQL 环境中实际跑一遍,印象会深很多
-- 有问题欢迎评论区留言,船长看到都会回复