首页/数据/SQL 速查/每组取前 N 行
聚合与窗口函数54 条

每组取前 N 行

分组 Top N 的标准解法:ROW_NUMBER(严格 N 行)或 RANK(含并列)编号后在外层过滤,窗口函数不能直接写进 WHERE。

SQL 速查数据更新于 2026-10-13

每组取前 N 行是什么

分组 Top N 的标准解法是先用窗口函数在组内编号,再在外层按编号过滤:ROW_NUMBER 严格返回 N 行,RANK 会保留并列名次因而可能多于 N 行,DENSE_RANK 不跳号。选择哪一个取决于业务对并列的处理:榜单严格限位用 ROW_NUMBER,允许并列用 RANK。窗口函数不能直接写在 WHERE 里,必须套一层子查询或 CTE,外层再按编号过滤,这是这一类查询的固定结构。

基本信息

基本结构内层编号,外层按编号过滤
ROW_NUMBER严格 N 行,并列也按顺序编号
RANK带并列且跳号,可能多于 N 行
DENSE_RANK带并列不跳号
分区与排序PARTITION BY 分组列,ORDER BY 排序列
版本要求MySQL 8.0+ 与 PostgreSQL 均支持窗口函数

典型使用场景

  • 每部门薪资前三:人事与薪酬分析
  • 每类目销量前十:电商榜单
  • 每个用户最近一次登录:去重取最新
  • 每组金额最大值所在行:取极值对应的完整记录
  • 每组前百分之二十:用 NTILE 分桶后过滤
  • 分页式取每组数据:按组内编号做游标切分

示例

示例
-- 每部门薪资前三:ROW_NUMBER 严格三行
SELECT * FROM (
    SELECT dept_id, emp_name, salary,
           ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
    FROM employees
) t
WHERE t.rn <= 3;

-- 允许并列:用 RANK,结果行数可能超过 3
SELECT * FROM (
    SELECT dept_id, emp_name, salary,
           RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rk
    FROM employees
) t
WHERE t.rk <= 3;

-- 每组取金额最大的一条完整记录
SELECT * FROM (
    SELECT o.*, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY amount DESC, id DESC) AS rn
    FROM orders o
) x WHERE x.rn = 1;

编号函数选择

函数行数结果适用
ROW_NUMBER严格等于 N榜单限位、去重取一条
RANK可能大于 N允许并列的排名场景
DENSE_RANK可能大于 N,名次连续分段与档位划分
NTILE(n)均分成 n 桶按比例分组取前一批
外层过滤必须写在子查询之外:窗口函数在同一层不可用于 WHERE,这是语法层面的限制。
排序键要能唯一确定顺序,否则并列行的编号不稳定,每次执行结果可能不同;补上主键做次级键即可。
分区与排序键上有索引时可以减少一次排序,但窗口函数本身的开销仍存在,大表上先过滤再开窗。

使用要点

  • 严格 N 行用 ROW_NUMBER,允许并列用 RANK
  • 排序键补主键,保证编号结果可复现
  • 先 WHERE 缩小范围再开窗,避免全表排序
  • 需要从每组取完整记录时,外层用主键再回表关联
  • 把编号逻辑放进 CTE,主查询只做过滤与展示
  • 验证时分别检查每组行数与总数是否符合预期

易错点

  • 把 ROW_NUMBER 或 RANK 写进 WHERE,语句直接报错
  • 该用 ROW_NUMBER 却用 RANK,并列时返回行数超过 N
  • 排序键不唯一导致每次保留的行不同,结果无法复现
  • 在超大表上不加任何过滤直接开窗分组,排序开销拖慢查询
  • 每组取一条时没有用主键做次级键,同一并列组内取到哪条不确定

数据来源与参考

  • MySQL 8.0 Reference Manual · Window Functions
  • PostgreSQL Documentation · Window Functions
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌