MySQL查询最大值(最小值)或前n个最大(最小)值的方法分析

在实际中,我们可能会有这样的需求,就是从数据库中查出某个字段的最大值或最小值,或者查出前n个最大(最小)值。例如,查出某个学生成绩中最好的一个或者查出排名在前5名的学生成绩等。下面简要给出一些示例查询代码。

我们的表结构如下图:
这里写图片描述

stuname字段相当于学生的姓名,score代表分数,id为主键 ;
表数据准备如下:
MySQL查询最大值(最小值)或前n个最大(最小)值的方法分析_第1张图片
一、查询最大(最小值)
在这里我们要分别查询出某个学生中的分数的最大值,这里可有两种查询方法。
1

SELECT a.stuname,MAX(a.score) AS score FROM stuscore a  GROUP BY a.`stuname` ;

在这个语句中,我们通过对stuname字段进行分组,然后再用MAX()函数对每组中的最大值进行计算。
2、使用连接

SELECT a.stuname,a.score AS score FROM stuscore a JOIN 
stuscore b ON a.`stuname`=b.`stuname` 
 GROUP BY a.`score` HAVING a.`score`=MAX(b.`score`);

在第二条sql语句中,我们以stuname为判断条件,对两个表进行连接操作。如果只执行SELECT a.stuname,a.score AS score FROM stuscore a JOIN
stuscore b ON a.
stuname=b.stuname我们会得到下面的结果集:
MySQL查询最大值(最小值)或前n个最大(最小)值的方法分析_第2张图片
之所以会得到这个结果集,是因为在连接时,左表中的每条记录都会根据连接条件ON a.stuname=b.stuname“和右表中的每条记录进行匹配,这就是为什么会出现重复记录的原因。这个有点像编程语言中的“双重循环”。

然后再加分组和分组条件GROUP BY a.score HAVING a.score=MAX(b.score),就可以得到每个学生中的最大分数;
这里写图片描述
其实GROUP BY a.`score ` HAVING a.`score`=MAX(b.`score`)这个分组条件一开始我也是有点不大理解,就是说我是根据score字段进行分组的,但这里的MAX()函数为什么就可以计算出每个学生中的最大分数,我们知道第一种方法比较好理解就是对每个学生的姓名分组,然后用MAX()函数计算出其中的最大值,这是肯定的,但第二方法是对score字段进行分组的。在这里我觉得原因应该是,在将两个表进行连接时,连接条件a.stuname=b.stuname` 已经对这些记录根据学生姓名进行分组了,所以MAX(b.score)`才可以计算出每个学生中的最大分数。
二、前n个最大(最小)值
在这里,我要查询出,每个学生中,排名前两位的分数。大概思路是,对以姓名为分组的每条记录,首先要查询某个记录在它所属的组别中,所有大于该记录的分数的记录数,然后根据到的记录数,就可以对这个结果集进行过虑了。首先来看代码和效果。

SELECT c.stuname,c.score FROM (SELECT a.stuname,a.score,(SELECT COUNT(*) FROM stuscore b WHERE b.score>a.score AND b.stuname=a.stuname) AS cnt 
FROM stuscore a ) c WHERE c.cnt<=1  GROUP BY c.stuname,c.score;

MySQL查询最大值(最小值)或前n个最大(最小)值的方法分析_第3张图片
这个确实得到了我们想要的结果集。
来分析 一下
在这个查询中的SELECT a.stuname,a.score,(SELECT COUNT(*) FROM stuscore b WHERE b.score>a.score AND b.stuname=a.stuname) AS cnt
FROM stuscore a;
子查询执行得到的结果集如下:
MySQL查询最大值(最小值)或前n个最大(最小)值的方法分析_第4张图片
cnt就是得到某个记录在它所属的组别中,所有大于该记录的分数的记录数。以姓名为zzz,分数为70的记录为例,它的cnt值为1就代表在它所属组别中,有1条记录的分数比它高。其中cnt字段的计算我们又可以通过一个子查询来完成SELECT COUNT(*) FROM stuscore b WHERE b.score>a.score AND b.stuname=a.stuname,有了这个子查询,当我们对每条记录进行计算时,都要计算这个子查询,这个子查询的条件WHERE b.score>a.score AND b.stuname=a.stuname就是限定在某个组别中查询比该条记录分数大的记录,同时通过COUNT()函数来计算符合条件的记录数。
最后再根据得到的结果集进行过滤,以WHERE c.cnt<=1为过滤条件(这里取出最大和次大的分数值),就可以得到正确的结果集了。

你可能感兴趣的:(后端)