核心要点

  • 窗口函数语法:函数() OVER(PARTITION BY 分组键 ORDER BY 排序键),不改变行数,按窗口给每行算一个值

  • 排名三件套:ROW_NUMBER() 严格 1,2,3 不并列;RANK() 并列后跳号;DENSE_RANK() 并列不跳号

  • 每组 Top-N 固定套路:先在子查询里用 ROW_NUMBER() 编号,再在外层 WHERE rn <= N 过滤

  • 易错点:WHERE 不能直接引用窗口函数别名,必须套一层子查询/CTE 再过滤

标准回答

窗口函数通过 OVER 子句在「不合并行」的前提下对每行做分组计算,PARTITION BY 决定分组、ORDER BY 决定组内顺序。求每组 Top-N 的标准做法是:用 ROW_NUMBER() 按分组键分区、按排序键降序编号,再在外层把 rn 小于等于 N 的行筛出来。注意排名函数必须写在子查询里,因为 WHERE 阶段早于窗口函数计算,不能直接对其别名做过滤。

sql
-- 求每个部门薪资最高的前 2 名员工
SELECT dept_id, emp_id, salary
FROM (
  SELECT
    dept_id,
    emp_id,
    salary,
    -- 按部门分区,组内薪资从高到低编号
    ROW_NUMBER() OVER (
      PARTITION BY dept_id
      ORDER BY salary DESC
    ) AS rn
  FROM employee
) t
WHERE rn <= 2          -- 每组只保留前 2 名
ORDER BY dept_id, salary DESC;

常见误区

⚠️ 常见踩坑

直接在 WHERE 里写 WHERE ROW_NUMBER() OVER(...) <= 2 会报错,因为窗口函数在 SELECT 后才计算;并列名次想保留全部时应用 RANK()/DENSE_RANK() 而非 ROW_NUMBER()。

追问

追问 1ROW_NUMBER、RANK、DENSE_RANK 在有并列值时输出有什么区别?

假设 ORDER BY 的值为 90,90,80:ROW_NUMBER 给 1,2,3(强行区分);RANK 给 1,1,3(并列后跳过 2);DENSE_RANK 给 1,1,2(并列后不跳号)。取「薪资前 3 高」要含并列时用 DENSE_RANK,取「任意 3 行」用 ROW_NUMBER。

追问 2如果要算每个部门的薪资累计占比,窗口函数怎么写?

用 SUM() OVER 做组内累计与总计:SUM(salary) OVER(PARTITION BY dept_id ORDER BY salary DESC) AS cum_sum 得到累计值,SUM(salary) OVER(PARTITION BY dept_id) AS total 得到部门总额,两者相除即累计占比。不写 ORDER BY 的 SUM OVER 是整组总和,写了 ORDER BY 则是从首行到当前行的滚动累计。

延伸学习

与本题相关的知识库文章、术语、工具与行业资讯。