Skip to content

窗口函数 ​

核心问题:窗口函数解决了什么问题?ROW_NUMBER、RANK、DENSE_RANK 有什么区别?

1. 它解决了什么问题? ​

在不改变结果行数的情况下,对每行数据进行跨行计算(如排名、累计、前后行对比)。没有窗口函数时,求每个部门薪资排名需要写复杂的自连接或子查询,性能差且难以维护。

2. 基本语法 ​

sql
函数名() OVER (
    PARTITION BY 分组列    -- 按哪个字段分组(类似 GROUP BY,但不合并行)
    ORDER BY 排序列        -- 窗口内的排序方式
    ROWS/RANGE BETWEEN ... -- 窗口帧范围(可选)
)

3. 常用窗口函数 ​

sql
-- 场景:查询每个部门的员工薪资排名
SELECT 
    name,
    department,
    salary,
    ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS row_num,
    RANK()       OVER (PARTITION BY department ORDER BY salary DESC) AS rank,
    DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank,
    LAG(salary)  OVER (PARTITION BY department ORDER BY salary DESC) AS prev_salary,
    LEAD(salary) OVER (PARTITION BY department ORDER BY salary DESC) AS next_salary,
    SUM(salary)  OVER (PARTITION BY department) AS dept_total,
    AVG(salary)  OVER (PARTITION BY department) AS dept_avg
FROM employees;

4. ROW_NUMBER vs RANK vs DENSE_RANK ​

窗口函数作用示例结果(薪资相同时)特点
ROW_NUMBER()连续排名(无并列)1, 2, 3, 4相同值也给不同排名,结果唯一
RANK()跳跃排名(有并列)1, 2, 2, 4相同值同排名,下一名跳过
DENSE_RANK()密集排名(有并列)1, 2, 2, 3相同值同排名,下一名不跳过

选择原则:

  • 需要唯一行号(如分页)→ ROW_NUMBER()
  • 需要体现并列且下一名跳过(如竞赛排名)→ RANK()
  • 需要体现并列且连续编号(如等级划分)→ DENSE_RANK()

5. LAG / LEAD:前后行对比 ​

sql
-- 计算每月销售额环比增长
SELECT 
    month,
    sales,
    LAG(sales) OVER (ORDER BY month) AS prev_month_sales,
    sales - LAG(sales) OVER (ORDER BY month) AS growth,
    ROUND(
        (sales - LAG(sales) OVER (ORDER BY month)) / 
        LAG(sales) OVER (ORDER BY month) * 100, 2
    ) AS growth_rate
FROM monthly_sales;
函数作用常见用途
LAG(col, n, default)取当前行前 n 行的值计算环比、同比
LEAD(col, n, default)取当前行后 n 行的值预测下一期、计算差值

6. 累计聚合 ​

sql
-- 计算累计销售额(Running Total)
SELECT 
    order_date,
    amount,
    SUM(amount) OVER (ORDER BY order_date) AS running_total,
    AVG(amount) OVER (ORDER BY order_date 
                      ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_avg_7d
FROM orders;

Released under the MIT License.