终极终极详细解释…因为比较详细,适合小白看(大佬请绕道)
代码比较简单,但是我一开始纠结了好久,脑壳疼
首先gruop by+limit 肯定是不可以的,下面演示一下
1.建表
CREATE TABLE `score_info` (
`id` BIGINT(18) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT NULL,
`class_no` VARCHAR(10) DEFAULT NULL,
`score` INT(5) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
INSERT INTO score_info VALUES(1, "张三", "高三1班", 90);
INSERT INTO score_info VALUES(2, "李四", "高三2班", 92);
INSERT INTO score_info VALUES(3, "王五", "高三3班", 88);
INSERT INTO score_info VALUES(4, "赵六", "高三1班", 65);
INSERT INTO score_info VALUES(5, "曹操", "高三2班", 99);
INSERT INTO score_info VALUES(6, "孙权", "高三3班", 85);
INSERT INTO score_info VALUES(7, "刘备", "高三1班", 90);
INSERT INTO score_info VALUES(8, "宋江", "高三2班", 94);
INSERT INTO score_info VALUES(9, "李广", "高三3班", 96);
INSERT INTO score_info VALUES(10, "张飞", "高三2班", 60);
听说你想gruop by+limit?真是太天真了(我猜每个人第一次遇到这问题都是这样想的)
SELECT * FROM score_info
GROUP BY class_no
ORDER BY score
LIMIT 2;
执行SELECT * FROM score_info GROUP BY class_no
你就知道已经错了
下面正式进入主题:左外连接实现topN:
#标准答案
SELECT si.*
FROM score_info si
LEFT JOIN score_info sii ON sii.class_no = si.class_no AND si.score < sii.score
GROUP BY si.id,si.class_no
HAVING COUNT(si.id) < 2
ORDER BY si.class_no, si.score DESC;
弄清楚原理最重要:
首先要知道左外连接【左连接】:左表加右表,如果右表不满足则为null
#执行下面三行
SELECT si.*,sii.*
FROM score_info si
LEFT JOIN score_info sii ON sii.class_no = si.class_no AND si.score < sii.score
这里取<
的用意是:如果有多个个同班级的同学分数比自己高的,就重复多少行。比如李四:曹操和宋江分数比李四高,就出现了两次,即COUNT(si.id)
=2
#执行下面四行
SELECT si.*,sii.* ,COUNT(si.id)
FROM score_info si
LEFT JOIN score_info sii ON sii.class_no = si.class_no AND si.score < sii.score
GROUP BY si.id,si.class_no #si.class_no也可以省略不写,因为id不可能重复
COUNT(si.id)=? 若?=1,说明他的分数全班最高或者有一个比他高(比如宋江:曹操比他高。曹操:全班最高),?>1说明同一个班级有?名同学的成绩比他要高,所以如果我们取top2,就要count(si.id)<2
那就很好理解为什么top2要加HAVING COUNT(si.id) < 2
了。接下来就是进行排序,让结果变得好看一点罢了。
取TOP1?——那就太简单了
SELECT si.* FROM score_info si
INNER JOIN
(SELECT class_no, MAX(score) max_s FROM score_info GROUP BY class_no) sii
ON si.class_no=sii.class_no AND si.`score` = max_s