SQL 窗口函数:一次讲清 row_number 和 rank 的区别

2026-10-14 · SQL

三个排序函数,名字很像,结果差很多。取「每组最新一条」时选错,会静默丢数据。

核心区别

函数遇到并列时结果示例
row_number()强制不并列,顺序由引擎决定1, 2, 3, 4
rank()并列同名次,后续名次跳号1, 1, 3, 4
dense_rank()并列同名次,后续名次不跳号1, 1, 2, 3

最容易出事的场景

需求:每个用户取最近一次下单记录。

-- ❌ 有风险:同一秒下两单时,rank 会给两行都打上 1,过滤后行数变多
SELECT * FROM (
  SELECT *, RANK() OVER (PARTITION BY uid ORDER BY created_at DESC) AS rn
  FROM orders
) t WHERE rn = 1;

-- ✅ 稳定:无论是否并列,每组只出一行
SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY uid ORDER BY created_at DESC, id DESC) AS rn
  FROM orders
) t WHERE rn = 1;

注意第二个写法里加上了 id DESC 作为第二排序键。这不只是为了消除并列, 更是为了让结果可复现 —— 不加的话,同一份数据两次执行可能给出不同的行。

什么时候该用 rank

当你需要「并列就都是第一」的语义时。比如排行榜:

-- 取销售额前 3 名,并列的都算进来
SELECT * FROM (
  SELECT name, amount,
         RANK() OVER (ORDER BY amount DESC) AS rk
  FROM sales
) t WHERE rk <= 3;

一个性能细节

窗口函数在 PARTITION BY 加 ORDER BY 之后需要排序。 如果表很大,先把无关列筛掉再开窗,比先开窗再筛快得多:

-- 好:先缩小数据集
WITH recent AS (
  SELECT uid, id, created_at FROM orders WHERE created_at >= '2026-10-01'
)
SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY uid ORDER BY created_at DESC, id DESC) rn
  FROM recent
) t WHERE rn = 1;
← 返回首页