必须用ROW_NUMBER()配合PARTITION BY和ORDER BY,缺一不可;否则无法实现分组内排序,仅全表编号或报错。

必须用 ROW_NUMBER() 配合 PARTITION BY 和 ORDER BY,缺一不可;否则不是分组内排序,只是全表编号或报错。
为什么直接加 ORDER BY 不行?
单纯在 SELECT 后写 ORDER BY 只影响最终结果输出顺序,不改变每行的逻辑位置,更无法生成“每个客户最新订单是第几条”这类组内序号。而 ROW_NUMBER() 的 ORDER BY 是窗口定义的一部分,决定编号依据——它和外层 ORDER BY 完全无关,可以不同。
- 常见错误:漏写
ORDER BY→ 报错"Window function requires an ORDER BY clause" - 常见错误:只写
ORDER BY不写PARTITION BY→ 全表从 1 编到 N,不是“每组从 1 开始” - 性能注意:
ORDER BY字段最好有索引,尤其当分区大、组内行多时(比如百万级用户各几百单)
怎么写才能让每个客户的订单按时间倒序排号?
核心是把业务语义准确映射到窗口参数:PARTITION BY customer_id 划分客户组,ORDER BY order_date DESC 决定组内谁排第一。
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC
) AS rn
FROM orders;- 返回结果中,每个
customer_id下的rn = 1行就是该客户的最新订单 - 如果想查“每个客户金额最高的订单”,就把
ORDER BY换成order_amount DESC - MySQL 8.0+、PostgreSQL、SQL Server、BigQuery、Databricks 均支持;MySQL 5.7 及更早不支持,会报错
"This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'"类似提示
如何用这个序号筛选“每组第一条”?
不能在同一个查询里直接 WHERE rn = 1 —— 窗口函数在 WHERE 之后执行,所以必须套一层子查询或 CTE。
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY order_date DESC
) AS rn
FROM orders
) t
WHERE t.rn = 1;- 这是去重最常用模式:保留每组按某规则排序后的首行
- 若需保留并列情况(比如两个订单同为最新时间),
ROW_NUMBER()会随机给 1 和 2,此时应改用RANK()或DENSE_RANK() - 注意别名
t.rn必须带表别名,否则部分引擎(如旧版 HiveSQL)会报"Column rn cannot be resolved"
ORDER BY 多字段时要注意什么?
当组内排序依据不止一个字段(例如“先按金额降序,金额相同时按时间升序”),多字段 ORDER BY 能确保结果稳定,避免因数据插入顺序不同导致 rn 分配不一致。
- 正确写法:
ORDER BY order_amount DESC, order_date ASC - 错误写法:
ORDER BY order_amount DESC, order_date(缺方向,默认 ASC,但显式写出更安全) - 特别提醒:如果排序字段含 NULL,默认排在最前(ASC)或最后(DESC),可能影响
rn = 1的归属,必要时用COALESCE(order_date, '1970-01-01')处理
真正容易被忽略的是:窗口函数计算发生在所有 JOIN、WHERE、GROUP BY 完成之后。这意味着你不能靠 ROW_NUMBER() 去“提前过滤”,它只能对已确定的数据集编号——所以先筛数据,再编号,顺序不能颠倒。

















