mysql 8.0+才支持row_number(),低版本会报错;需用partition by分组+order by排序才能实现分组内编号,仅order by为全表排序。

MySQL 8.0+ 才有 ROW_NUMBER(),低版本会报错
MySQL 在 8.0 版本才正式支持窗口函数,如果你用的是 5.7 或更早版本,直接写 ROW_NUMBER() OVER (...) 会触发 ERROR 1305 (42000): FUNCTION xxx.ROW_NUMBER does not exist。别折腾自定义变量模拟——逻辑易错、并发不安全、ORDER BY 和 LIMIT 行为不可靠。确认版本最简单的方式是执行:
SELECT VERSION();输出以
8.0. 开头才算可用。分组内排序取前3,ROW_NUMBER() 必须配合 PARTITION BY 和 ORDER BY
只写 ORDER BY 不够,那只是全表排序;漏掉 PARTITION BY 就变成给整张表编号,不是“每组内”。典型写法是:
SELECT * FROM (
SELECT
category,
product_name,
sales,
ROW_NUMBER() OVER (
PARTITION BY category
ORDER BY sales DESC
) AS rn
FROM products
) t WHERE rn
-
PARTITION BY category:按品类分组,每组独立编号 -
ORDER BY sales DESC:组内按销量降序,确保最高销量排第1 - 外层
WHERE rn 过滤,不能写在窗口函数里 - 注意:如果存在并列(如两个产品销量同为最高),
ROW_NUMBER()仍会强制给不同序号(比如 1,2,3),不会跳过或并列
要处理并列情况?换 RANK() 或 DENSE_RANK()
当业务要求“销量并列第1的两个产品都算前3”,ROW_NUMBER() 就不合适了。比如某组销量为 [100,100,90,80]:
-
ROW_NUMBER()给出1,2,3,4→ 只取前3会漏掉第二个 100 -
RANK()给出1,1,3,4→WHERE rn 能取到三个结果(含两个并列第1) -
DENSE_RANK()给出1,1,2,3→ 更紧凑,适合“去重后排名”场景
替换只需改函数名:RANK() OVER (PARTITION BY category ORDER BY sales DESC),其余结构完全一致。
性能敏感时,务必给 PARTITION BY + ORDER BY 字段建联合索引
窗口函数执行时,MySQL 需要按分组和排序字段快速定位数据块。没索引会导致全表扫描 + 临时文件排序,大数据量下可能慢几秒甚至超时。例如上面例子,应在 products 表上建:
ALTER TABLE products ADD INDEX idx_category_sales (category, sales DESC);
- 顺序很重要:先
PARTITION BY字段,再ORDER BY字段 -
DESC显式声明可让索引更好匹配降序需求(MySQL 8.0+ 支持降序索引) - 如果查询还带其他
WHERE条件(如WHERE status = 'active'),需评估是否扩展为三字段索引
真正容易被忽略的是索引顺序和降序声明——很多人建了 (sales, category) 或漏写 DESC,结果执行计划里依然出现 Using filesort。











