从表中查询多列时,如何仅对单列应用TOP语句?
精准筛选指定科目前N高分记录的正确姿势
看起来你遇到的问题是想精准抓取某科目下的前3高分记录,咱们一步步拆解之前的问题,再给出靠谱的解法:
先说说你之前的两个查询为啥出问题
第一个查询报错的原因:
当使用IN子句时,子查询只能返回单个列,但你写了select top (3) *,一下子返回了roll_no、marks、subject多列,数据库根本不知道用哪一列去匹配marks in (...),所以直接抛出了那个错误。第二个查询返回多余数据的原因:
你把子查询改成select top (3) marks后,虽然解决了列数问题,但主查询没加subject的限定条件!也就是说,子查询只是拿到了目标科目前3的分数值,但主查询会把所有科目里分数等于这些值的记录都捞出来,这就导致其他科目的高分混进来了。
正确的解决方案
推荐用窗口函数(支持SQL Server、MySQL 8.0+、PostgreSQL等主流数据库),这是目前最简洁准确的方式,还能灵活处理分数并列的情况:
方案1:严格取前3条(忽略并列,只取到第3个)
如果你的需求是不管分数是否并列,只取前3条记录,用ROW_NUMBER():
SELECT roll_no, marks AS Percentage FROM ( SELECT roll_no, marks, -- 按科目分组,分数倒序排序,给每条记录编唯一序号 ROW_NUMBER() OVER (PARTITION BY subject ORDER BY marks DESC) AS row_num FROM database WHERE subject = 'Maths' -- 替换成你要查询的科目 ) ranked_records WHERE row_num <= 3 ORDER BY Percentage DESC;
注意:如果分数相同,序号会随机分配,要是想让排序更稳定,可以再加个字段,比如ORDER BY marks DESC, roll_no ASC,按学号正序排相同分数的记录
方案2:包含所有并列的前3名
如果希望把分数并列的记录都包含进来(比如3个学生都是第3名,这3个都要返回),用RANK():
SELECT roll_no, marks AS Percentage FROM ( SELECT roll_no, marks, -- 按科目分组,相同分数会得到相同排名 RANK() OVER (PARTITION BY subject ORDER BY marks DESC) AS rank_num FROM database WHERE subject = 'Maths' -- 替换成你要查询的科目 ) ranked_records WHERE rank_num <= 3 ORDER BY Percentage DESC;
兼容老版本数据库的方案(不用窗口函数)
如果你用的是不支持窗口函数的老版本数据库(比如MySQL 5.x),可以用关联子查询实现类似效果:
SELECT roll_no, marks AS Percentage FROM database d1 WHERE subject = 'Maths' -- 替换成目标科目 AND ( -- 统计当前记录的分数在同科目中的排名(按不同分数算排名) SELECT COUNT(DISTINCT marks) FROM database d2 WHERE d2.subject = d1.subject AND d2.marks >= d1.marks ) <= 3 ORDER BY Percentage DESC;
测试你的示例数据
用你给的示例数据查询English科目:
- 用
RANK()的话,会返回roll_no 10002、10003、10004(他们都是75分,并列第1) - 用
ROW_NUMBER()的话,会按你指定的排序规则返回其中3条(如果没加额外排序字段,顺序可能随机)
内容的提问来源于stack exchange,提问作者Anita Mathew




