查询单个表中每个组中的前 5 名候选人
Posted
技术标签:
【中文标题】查询单个表中每个组中的前 5 名候选人【英文标题】:Query for getting top 5 candidate in every group in single table 【发布时间】:2018-01-11 10:01:51 【问题描述】:我有一个表格,其中列出了每个科目的学生分数,我必须以这样一种方式进行查询,以便我能够获得每个科目中所有获得最高分的前 5 名学生。
这是一个示例表:
我的预期输出看起来像:
PCM、ART、PCB的前五名学生,根据学生成绩,如果有两个或更多学生安全与这些记录相同,也需要在单个查询列表中。
【问题讨论】:
请提供表结构 请参阅如何创建最小、完整和可验证的示例***.com/help/mcve @Saurabh Mistry 希望这能消除您的疑虑。 每门学科的前 5 名学生在最高分的基础上得到保障 Get top n records for each group of grouped results的可能重复 【参考方案1】:原答案
从技术上讲,使用单个 SQL
查询无法实现您想要完成的任务。如果您只希望每个学科有一个学生,您可以使用 GROUP BY
实现这一目标,但在您的情况下它不会起作用。
我能想到的让每个科目有 5 名学生的唯一方法是编写 x
查询,每个科目一个查询,然后使用 UNION
将它们粘合在一起。这样的查询最多会返回 5x
行。
由于您想根据分数获得前 5 名学生,因此您必须使用 ORDER BY
子句,它与 UNION
子句结合使用会导致错误。为避免这种情况,您将不得不使用子查询,以便 UNION
和 ORDER BY
子句不在同一级别。
查询:
-- Select the 5 students with the highest mark in the `PCM` subject.
(
SELECT *
FROM student
WHERE subject = 'PCM'
ORDER BY studentMarks DESC
LIMIT 5
)
UNION
(
SELECT *
FROM student
WHERE subject = 'PCB'
ORDER BY studentMarks DESC
LIMIT 5
)
UNION
(
SELECT *
FROM student
WHERE subject = 'ART'
ORDER BY studentMarks DESC
LIMIT 5
);
查看this SQLFiddle 自行评估结果。
更新答案
本次更新旨在让超过 5 名学生在许多学生在特定科目中成绩相同的情况下获得。
我们不使用LIMIT 5
来获得前5 行,而是使用LIMIT 4,1
来获得第五高的成绩,并使用它来获得在给定科目中成绩高于或等于该成绩的所有学生。但是,如果一个科目中有 LIMIT 4,1 将返回 NULL
。在这种情况下,我们基本上想要每个学生,所以我们使用最低成绩。
要实现上述目的,您需要使用以下代码 x
次,与您拥有的主题一样多,并使用 UNION
将它们连接在一起。很容易理解,这个解决方案可以用于少数不同的主题,否则查询的范围将变得无法维护。
代码:
-- Select the students with the top 5 highest marks in the `x` subject.
SELECT *
FROM student
WHERE studentMarks >= (
-- If there are less than 5 students in the subject return them all.
IFNULL (
(
-- Get the fifth highest grade.
SELECT studentMarks
FROM student
WHERE subject = 'x'
ORDER BY studentMarks DESC
LIMIT 4,1
), (
-- Get the lowest grade.
SELECT MIN(studentMarks)
FROM student
WHERE subject = 'x'
)
)
) AND subject = 'x';
查看this SQLFiddle 自行评估结果。
替代方案:
经过一些研究,我发现了一种替代的、更简单的查询,它可以根据您提供的数据产生与上述结果相同的结果,而无需“硬编码”每个主题查询。
在以下解决方案中,我们定义了几个变量来帮助我们控制数据:
一个缓存上一行的主题和 用于保存增量值,以区分具有相同主题的行。查询:
-- Select the students having the top 5 marks in each subject.
SELECT studentID, studentName, studentMarks, subject FROM
(
-- Use an incremented value to differentiate rows with the same subject.
SELECT *, (@n := if(@s = subject, @n +1, 1)) as n, @s:= subject
FROM student
CROSS JOIN (SELECT @n := 0, @s:= NULL) AS b
) AS a
WHERE n <= 5
ORDER BY subject, studentMarks DESC;
查看this SQLFiddle 自行评估结果。
想法来自以下线程:
Get top n records for each group of grouped results
How to SELECT the newest four items per category?
Select X items from every type
Getting the latest n records for each group
【讨论】:
通过这个查询,每组只返回五行,如果两个或多个学生获得相同的分数怎么办。所以限制不是个好主意。 你看到了吗?当我说你没有很好地解释你想要什么时,这就是我的意思。您的问题最初并未提出这样的要求。我已经更新了我的答案以涵盖这种情况@Kandy?告诉我它是否如你所愿。 感谢您的耐心等待。下次我肯定会尝试以更好的方式和更好的格式来解释我的问题。再次感谢。 它是否如你所愿@Kandy?如果是这样,请善待它,为这项工作投赞成票。根据您提供的数据,它按预期运行。 不客气@Kandy,你说得对,很难维持如此规模的查询,尤其是在处理大量不同主题时。再看看我的答案,因为我更新了它以包含一个更小但等效的查询,可以轻松维护。我还提供了一些指向类似问题的链接,以便您也可以从那里的答案中受益。【参考方案2】:下面的查询几乎产生了我想要的结果,希望这个查询将来可以帮助其他人。
SELECT a.studentId, a.studentName, a.StudentMarks,a.subject FROM testquery AS a WHERE
(SELECT COUNT(*) FROM testquery AS b
WHERE b.subject = a.subject AND b.StudentMarks >= a.StudentMarks) <= 2
ORDER BY a.subject ASC, a.StudentMarks DESC
【讨论】:
以上是关于查询单个表中每个组中的前 5 名候选人的主要内容,如果未能解决你的问题,请参考以下文章