You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

从表中查询多列时,如何仅对单列应用TOP语句?

精准筛选指定科目前N高分记录的正确姿势

看起来你遇到的问题是想精准抓取某科目下的前3高分记录,咱们一步步拆解之前的问题,再给出靠谱的解法:

先说说你之前的两个查询为啥出问题

  1. 第一个查询报错的原因
    当使用IN子句时,子查询只能返回单个列,但你写了select top (3) *,一下子返回了roll_nomarkssubject多列,数据库根本不知道用哪一列去匹配marks in (...),所以直接抛出了那个错误。

  2. 第二个查询返回多余数据的原因
    你把子查询改成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

火山引擎 最新活动