MySQL如何实现OVER(PARTITION BY)分区排名?SQL Server可行遇阻求助
搞定MySQL分区内的自定义排名需求
嘿,从你给的示例和需求来看,你要的是按Name分组,每个组里把Price从高到低排,然后给每行分配一个从1开始往上数的排名——最大的Price对应组里最大的排名数,最小的对应1,而且相同Price的行也会按顺序拿到连续的排名(不会并列)。
下面分两种MySQL版本给你具体的实现方案:
一、MySQL 8.0+(推荐,支持窗口函数)
MySQL 8.0及以上已经支持OVER(PARTITION BY)这类窗口函数,和SQL Server的语法几乎一致,实现起来很简单。你可以用两种思路来写:
思路1:反转降序行号
先给每个分组内按Price降序分配行号,再用分组总行数减去行号得到目标排名:
SELECT Name, Price, COUNT(*) OVER(PARTITION BY Name) - ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Price DESC) + 1 AS Rank FROM MyTable ORDER BY Name, Price DESC;
思路2:直接升序行号(更简洁)
既然最小的Price对应排名1,那直接按Price升序给每个分组分配行号,最后再按Name和Price降序输出即可:
SELECT Name, Price, ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Price ASC) AS Rank FROM MyTable ORDER BY Name, Price DESC;
两种写法都能得到你想要的结果,思路2更直观易懂,推荐使用。
二、MySQL 5.7及以下(不支持窗口函数)
如果你的MySQL版本还停留在5.7或更早,就得用用户变量来模拟分区排名的逻辑:
SELECT Name, Price, @rank := CASE WHEN @current_name = Name THEN @rank + 1 ELSE 1 END AS Rank, @current_name := Name FROM ( -- 先按分组和Price升序排序,确保最小Price排在组内最前面 SELECT Name, Price FROM MyTable ORDER BY Name, Price ASC ) AS t, -- 初始化变量:记录当前分组名称和当前排名 (SELECT @current_name := '', @rank := 0) AS vars -- 最后按分组和Price降序输出,匹配期望格式 ORDER BY Name, Price DESC;
逻辑解释:
- 子查询
t先把数据按Name分组、Price升序排序,这样每个组里最小的Price会排在最前面 - 用
@current_name跟踪当前处理的分组名称,@rank记录当前组的排名序号 - 当处理的行和上一行属于同一个分组时,排名加1;如果切换到新分组,排名重置为1
- 外层查询再按
Name和Price降序排序,输出你期望的格式
验证结果
不管用哪种方案,执行后都会得到你想要的结果:
Name | Price | Rank
abs | 200 | 4
abs | 100 | 3
abs | 60 | 2
abs | 10 | 1
qwe | 50 | 4
qwe | 25 | 3
qwe | 10 | 2
qwe | 10 | 1
trx | 20 | 2
trx | 19 | 1
内容的提问来源于stack exchange,提问作者s-dept
相关产品推荐
相关产品推荐

