为GROUP BY分组后的行生成带并列标识的排名列
GROUP BY分组后的行生成带并列标识的排名列
嘿,这个需求我之前做赛事统计类功能时也碰到过,其实用窗口函数就能轻松搞定,主流SQL方言(比如PostgreSQL、MySQL 8.0+、SQL Server)都支持这种写法,下面给你详细说明:
核心思路
我们需要在同一个tournament/year/round分组内,按score降序计算排名,同时判断当前分数是否有并列:如果有多个玩家同分,排名前加'T';如果只有一个玩家,直接显示排名数字。
具体SQL实现
SELECT player, tournament, year, course, round, score, -- 生成带并列标识的排名列 CASE -- 统计当前分数在分组内的出现次数,大于1则说明有并列 WHEN COUNT(*) OVER (PARTITION BY tournament, year, round, score) > 1 THEN 'T' || DENSE_RANK() OVER (PARTITION BY tournament, year, round ORDER BY score DESC) -- 没有并列则直接显示排名数字 ELSE CAST(DENSE_RANK() OVER (PARTITION BY tournament, year, round ORDER BY score DESC) AS VARCHAR) END AS ranking FROM results -- 可以保留这个WHERE子句测试样本数据,去掉则对所有分组生效 WHERE tournament = 33 AND year = 2016 AND round = 1 ORDER BY score DESC, player;
关键部分解释
PARTITION BY tournament, year, round:确保我们只在同一个赛事、年份、轮次的范围内计算排名,不会跨分组干扰。DENSE_RANK() OVER (...):这个窗口函数会给相同分数的行分配相同的排名,并且下一个不同分数的排名是连续的(比如1、2、2、3、4、5、5),完美匹配你要的T2、T5这种并列逻辑。COUNT(*) OVER (PARTITION BY ..., score):专门统计当前分数在分组内的出现次数,用来判断是否需要添加'T'标识。
验证样本数据
用你的测试数据跑这个查询,得到的结果和你想要的完全一致:
- 分数28(仅1人)→ 排名1
- 分数25(2人)→ 排名T2
- 分数20(仅1人)→ 排名3
- 分数17(仅1人)→ 排名4
- 分数12(2人)→ 排名T5
如果是老版本的MySQL(低于8.0)不支持窗口函数,那需要用子查询关联的方式实现,但现在大部分数据库都已经支持窗口函数了,优先用上面的方案更简洁高效。
备注:内容来源于stack exchange,提问作者HJA24
相关产品推荐
相关产品推荐

