mysql中top问题(取前X名数据)

mysql中top问题(取前X名)

前置数据

建表语句

CREATE TABLE `student_info` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `no` int DEFAULT NULL COMMENT '学号',
  `subject_name` varchar(255) DEFAULT NULL COMMENT '科目名称',
  `score` int DEFAULT NULL COMMENT '得分',
  PRIMARY KEY (`id`),
  KEY `idx_subject` (`subject_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

创建数据语句

INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (1, 1, 'math', 91);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (2, 1, 'chinese', 90);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (3, 1, 'english', 87);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (4, 1, 'physics', 86);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (5, 1, 'chemistry', 88);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (6, 2, 'math', 85);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (7, 2, 'chinese', 84);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (8, 2, 'english', 83);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (9, 2, 'physics', 91);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (10, 2, 'chemistry', 91);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (11, 3, 'math', 76);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (12, 3, 'chinese', 45);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (13, 3, 'english', 32);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (14, 3, 'physics', 12);
INSERT INTO `student_info` (`id`, `no`, `subject_name`, `score`) VALUES (15, 3, 'chemistry', 40);

目的

  • 获取每个学生最高的三门成绩

MySQL版本在8及以上

使用窗口函数

RANK()
执行SQL
SELECT 
* 
FROM 
(SELECT `no`,subject_name,score,RANK() over(partition by `no` ORDER BY score desc) `rank_num` FROM student_info)t 
WHERE t.rank_num <=3 
ORDER BY `no`,rank_num
执行结果
nosubject_namescorerank_num
1math911
1chinese902
1chemistry883
2physics911
2chemistry911
2math853
3math761
3chinese452
3chemistry403
DENSE_RANK()
执行SQL
SELECT 
* 
FROM (SELECT `no`,subject_name,score,DENSE_RANK() over(partition by `no` ORDER BY score desc) `rank_num` FROM student_info)t 
WHERE t.rank_num <=3
 ORDER BY `no`,rank_num
执行结果
nosubject_namescorerank_num
1math911
1chinese902
1chemistry883
2physics911
2chemistry911
2math852
2chinese843
3math761
3chinese452
3chemistry403
ROW_NUMBER()
执行SQL
SELECT 
* 
* FROM (SELECT `no`,subject_name,score,ROW_NUMBER() over(partition by `no` ORDER BY score desc) `rank_num` FROM student_info)t 
* WHERE t.rank_num <=3 
* ORDER BY `no`,rank_num
执行结果
nosubject_namescorerank_num
1math911
1chinese902
1chemistry883
2physics911
2chemistry912
2math853
3math761
3chinese452
3chemistry403
注意
  • RANK、DENSE_RANK计算排名,如果值相同,会并列排名,ROW_NUMBER只是按顺序加序号,跟实际值无关
  • RANK会得到它前面所有记录行数的名次,比如前面两个第一名,它就是第三名。DENSE_RANK只会计算它前面有效值的行数,比如前面只有两个100分,它的99分就是第二名。
  • 所以RANK、DENSE_RANK都会有获取到过多数据的可能
  • ROW_NUMBER在第一个查询语句指定排序不生效,在窗口函数内指定多个字段排序会导致结果异常
  • ROW_NUMBER并列计算值的顺序是否有可能不一致未确认,在作者当前示例中多次执行未发现结果有变化,查看结果是根据主键ID升序。如果没有主键ID结果未知

MySQL版本在8以下

使用子查询

执行SQL
SELECT 
e1.`no`,e1.subject_name,e1.score,
(SELECT count(e2.score) FROM	student_info AS e2 	WHERE	e1.score < e2.score	AND e1.no = e2.no)+1 rank_num
FROM
	student_info AS e1
WHERE
(SELECT  count(e2.score) FROM	student_info AS e2  WHERE	e1.score < e2.score	AND e1.no = e2.no) < 3
ORDER BY `no`,score DESC
执行结果
nosubject_namescorerank_num
1math911
1chinese902
1chemistry883
2physics911
2chemistry911
2math853
3math761
3chinese452
3chemistry403
注意
  • 可以看到有并列名次,在特殊情况下还是会取到过多的数据,比如获取前一条

使用变量

执行SQL
SELECT  
`no`,subject_name,score,rank_num 
FROM
( SELECT s.subject_name,s.score,@rankn:= IF(@sn = `no`,@rankn+1,1) rank_num,(@sn := `no`) `no` 
	FROM 
	student_info s,(SELECT @rankn:=1,@sn := '')t
	ORDER BY s.`no`,s.score DESC )t2
WHERE t2.rank_num <= 3
ORDER BY t2.`no`,t2.rank_num
执行结果
nosubject_namescorerank_num
1math911
1chinese902
1chemistry883
2physics911
2chemistry912
2math853
3math761
3chinese452
3chemistry403
注意
  • 对于@sn变量的设置必须要在@rankn变量之后,这两列的顺序不能颠倒
  • 18
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抓取网易云音乐Top200的歌并写入MySQL数据库可以分为以下几个步骤: 1. 通过Python的Requests模块向网易云音乐Top200的网页发送请求,获取网页HTML代码。 2. 使用Python的BeautifulSoup库解析HTML代码,获取歌曲称。 3. 使用Python的pymysql库连接MySQL数据库,并创建一个表格用于存储歌曲称。 4. 将解析得到的歌曲称插入到MySQL数据库。 下面是具体的代码实现: ```python import requests from bs4 import BeautifulSoup import pymysql # 请求网页 url = 'https://music.163.com/discover/toplist?id=19723756' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} response = requests.get(url, headers=headers) # 解析HTML代码 soup = BeautifulSoup(response.text, 'html.parser') songs = soup.select('ul.f-hide li a') # 连接MySQL数据库 conn = pymysql.connect(host='localhost', port=3306, user='root', password='123456', db='testdb') cursor = conn.cursor() # 创建表格 cursor.execute('create table if not exists songlist (id int auto_increment primary key, name varchar(100))') # 插入数据 for song in songs: name = song.text cursor.execute('insert into songlist(name) values (%s)', (name,)) conn.commit() # 关闭数据库连接 cursor.close() conn.close() ``` 运行上述代码后,MySQL数据库就会生成一个为“songlist”的表格,其包含了网易云音乐Top200的歌曲称。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值