查询在单个表中获取每个组中的前5个候选者

时间:2018-01-11 10:01:51

标签: mysql

我有一张桌子,其中学生在每个科目中进行标记,我必须以这样的方式获得查询,以便我能够在每个获得最高分的科目中获得所有前5名学生。

以下是一个示例表:

enter image description here

我的预期输出看起来像:

基于学生分数的PCM,ART,PCB的前五名学生,如果两个或更多学生的安全性与那些记录相同,也需要在列表中进行单一查询。

2 个答案:

答案 0 :(得分:2)

原始答案

从技术上讲,使用单个SQL查询无法实现您想要完成的任务。如果您只想要每个科目一名学生,您可以使用GROUP BY实现这一目标,但在您的情况下,它将无效。

我能想到让每个主题有5名学生的唯一方法是编写 x 查询,每个主题一个,并使用UNION将它们粘合在一起。此类查询最多会返回 5x 行。

由于您希望根据该标记获得前5名学生,因此您必须使用ORDER BY子句,该子句与UNION子句一起将导致错误。为避免这种情况,您必须使用子查询,以便UNIONORDER 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来获得排名第五的成绩,而不是使用LIMIT 4,1来获得排名第五的成绩,并使用它来获得所有等级高于或等于给定成绩的学生学科。但是,如果有&lt;主题LIMIT 4,1中的5名学生将返回NULL。在这种情况下,我们基本上需要每个学生,所以我们使用最低成绩。

要实现上述目标,您需要使用以下代码 x 次,与您拥有的主题一样多,并使用{{1}将它们连接在一起}。可以很容易地理解,这个解决方案可以用于少数不同的主题,或者查询的范围将变得不可维护。

<强>代码:

UNION

查看this SQLFiddle以自行评估结果。

<强>替代:

经过一些研究后,我发现了一个替代的,更简单的查询,它将根据您提供的数据产生与上面提供的相同的结果,而不需要“硬编码”每个主题都在自己的查询。

在下面的解决方案中,我们定义了几个帮助我们控制数据的变量:

  • 一个缓存前一行的主题和
  • 一个用于保存增量值,以区分具有相同主题的行。

<强>查询:

-- 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以自行评估结果。

以下主题采取了一些想法:

答案 1 :(得分:0)

以下查询几乎可以产生我想要的内容,这个查询可能会在将来帮助其他人。

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