优先用rank(),因业务常需允许并列的“前三名”;rank()对相同销量给同名次且跳过后续名次,row_number()则强制连续不重复;mysql 8.0+/postgresql支持,旧版需模拟且性能差。

用 RANK() 还是 ROW_NUMBER()?关键看并列怎么算
销量并列时要不要占名额,直接决定函数选型:RANK() 对相同销量给相同排名,跳过后续名次(比如两个第1名,下一个就是第3名);ROW_NUMBER() 强制不重复,按顺序硬排(两个第1名会变成第1、第2名)。多数业务场景要求“前三名商品”,允许并列,所以优先用 RANK()。
实操建议:
- 先按分类分组,再按销量降序排序:
ORDER BY sales DESC - 窗口定义必须写全:
PARTITION BY category,漏掉就变成全局排名 - 别在
WHERE里直接过滤排名——窗口函数不能在WHERE中引用,得套一层子查询或 CTE
MySQL 8.0+ 和 PostgreSQL 写法几乎一致,但旧版 MySQL 不行
MySQL 5.7 及更早版本不支持窗口函数,强行写会报错 ERROR 1064。确认版本:运行 SELECT VERSION();。若低于 8.0,只能用自连接或变量模拟,稳定性差、性能低,不推荐。
标准写法(MySQL 8.0+/PostgreSQL):
SELECT category, product_name, sales
FROM (
SELECT category, product_name, sales,
RANK() OVER (PARTITION BY category ORDER BY sales DESC) AS rk
FROM sales_table
) t
WHERE rk
<h3>查出来结果比预期多?大概率是没去重或数据有脏值</h3>
<p>常见错误现象:某分类返回 5 条记录,但销量前三只该有 3 个——往往因为同一商品在不同时间/渠道有多条销售记录,没聚合就直接排名。</p>
<p>使用场景判断:</p>
- 原始表是明细订单表(每行一条订单)→ 必须先
GROUP BY category, product_name汇总SUM(sales),再开窗 - 原始表已是按商品聚合的宽表 → 可直接开窗
- 销量字段含
NULL或负数?ORDER BY sales DESC会让NULL排最前(SQL 标准行为),需加WHERE sales IS NOT NULL AND sales > 0
性能卡在大表上?注意索引和分区字段对齐
窗口函数本身不走索引,但 PARTITION BY 和 ORDER BY 字段是否命中索引,极大影响执行速度。比如 PARTITION BY category ORDER BY sales DESC,理想索引是 (category, sales)(顺序不能反)。
性能影响点:
- 没索引时,MySQL 可能触发临时表 + filesort,100 万行以上明显变慢
- PostgreSQL 对窗口函数优化更好,但同样依赖索引覆盖
- 如果分类数极少(如只有 5 个大类),考虑用
UNION ALL分类查,有时比单个窗口更快
GROUP BY 清干净,再进窗口,省事又准确。











