这种针对每组展开处理的功能就叫窗口函数,有的数据库叫分析函数。
在 MySQL 8.0 之前,大家想要得到这样的结果,就得用以下几种方法来实现:
1. session 变量
2. group_concat 函数组合
3. 自己写 store routines
接下来大家用经典的 学生/课程/成绩 来做窗口函数演示
准备
学生表
mysql> show create table student \G*************************** 1. row ***************************Table: studentCreate Table: CREATE TABLE student (sid int(10) unsigned NOT NULL,sname varchar(64) DEFAULT NULL,PRIMARY KEY (sid)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec)
课程表
mysql> show create table course\G*************************** 1. row ***************************Table: courseCreate Table: CREATE TABLE `course` (`cid` int(10) unsigned NOT NULL,`cname` varchar(64) DEFAULT NULL,PRIMARY KEY (`cid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec)
成绩表
mysql> show create table score\G*************************** 1. row ***************************Table: scoreCreate Table: CREATE TABLE `score` (`sid` int(10) unsigned NOT NULL,`cid` int(10) unsigned NOT NULL,`score` tinyint(3) unsigned DEFAULT NULL,PRIMARY KEY (`sid`,`cid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci1 row in set (0.00 sec)
测试数据
mysql> select * from student;+———–+————–+| sid | sname |+———–+————–+| 10001 | 张三 || 10002 | 李四 || 10003 | 武松 || 10004 | 潘金莲 || 10005 | 菠菜 || 10006 | 杨发财 || 10007 | 欧阳修 || 10008 | 郭靖 || 10009 | 黄蓉 || 10010 | 东方不败 |+———–+————–+10 rows in set (0.00 sec)mysql> select * from score;;+———–+———-+——-+| sid | cid | score |+———–+———-+——-+| 10001 | 2001 | 50 || 10001 | 2002 | 88 || 10001 | | 54 || 10001 | | 43 || 10001 | | 89 || 10002 | 2001 | 79 || 10002 | 2002 | 97 || 10002 | | 82 || 10002 | | 85 || 10002 | | 80 || 10003 | 2001 | 48 || 10003 | 2002 | 98 || 10003 | | 47 || 10003 | | 41 || 10003 | | 34 || 10004 | 2001 | 81 || 10004 | 2002 | 69 || 10004 | | 67 || 10004 | | 99 || 10004 | | 61 || 10005 | 2001 | 40 || 10005 | 2002 | 52 || 10005 | | 39 || 10005 | | 74 || 10005 | | 86 || 10006 | 2001 | 42 || 10006 | 2002 | 52 || 10006 | | 36 || 10006 | | 58 || 10006 | | 84 || 10007 | 2001 | 79 || 10007 | 2002 | 43 || 10007 | | 79 || 10007 | | 98 || 10007 | | 88 || 10008 | 2001 | 45 || 10008 | 2002 | 65 || 10008 | | 90 || 10008 | | 89 || 10008 | | 74 || 10009 | 2001 | 73 || 10009 | 2002 | 42 || 10009 | | 95 || 10009 | | 46 || 10009 | | 45 || 10010 | 2001 | 58 || 10010 | 2002 | 52 || 10010 | | 55 || 10010 | | 87 || 10010 | | 36 |+———–+———-+——-+50 rows in set (0.00 sec)mysql> select * from course;+———-+————+| cid | cname |+———-+————+| 2001 | mysql || 2002 | oracle || | postgresql || | mongodb || | dble |+———-+————+5 rows in set (0.00 sec)
MySQL 8.0 之前
比如大家求成绩排名前三的学生排名,偶来举个用 session 变量和 group_concat 函数来分别实现的例子:
session 变量方式
每组开始赋一个初始值序号和初始分组字段。
ame,a.sname,c.score, c.ranking_scoreFROMstudent a,course b,(SELECTc.*,IF(@cid = c.cid,@rn := @rn + 1,@rn := 1) AS ranking_score,@cid := c.cid AS tmpcidFROM(SELECT*FROMscoreORDER BY cid,score DESC) c,(SELECT@rn := 0 rn,@cid := ”) initialize_table) cWHERE a.sid = c.sidAND b.cid = c.cidAND c.ranking_score <= 3ORDER BY ame,c.ranking_score;+————+———–+——-+—————+| cname | sname | score | ranking_score |+————+———–+——-+—————+| dble | 张三 | 89 | 1 || dble | 欧阳修 | 88 | 2 || dble | 菠菜 | 86 | 3 || mongodb | 潘金莲 | 99 | 1 || mongodb | 欧阳修 | 98 | 2 || mongodb | 郭靖 | 89 | 3 || mysql | 李四 | 100 | 1 || mysql | 潘金莲 | 81 | 2 || mysql | 欧阳修 | 79 | 3 || oracle | 武松 | 98 | 1 || oracle | 李四 | 97 | 2 || oracle | 张三 | 88 | 3 || postgresql | 黄蓉 | 95 | 1 || postgresql | 郭靖 | 90 | 2 || postgresql | 李四 | 82 | 3 |+————+———–+——-+—————+15 rows in set, 5 warnings (0.01 sec)
group_concat 函数方式
利用 findinset 内置函数来返回下标作为序号使用。
SELECT*FROM(ame,a.sname,c.score,FIND_IN_SET(c.score, d.gp) score_rankingFROMstudent a,course b,score c,(SELECTcid,GROUP_CONCAT(scoreORDER BY score DESC SEPARATOR ‘,’) gpFROMscoreGROUP BY cidORDER BY score DESC) dWHERE a.sid = c.sidAND b.cid = c.cidAND c.cid = d.cidORDER BY d.cid,score_ranking) yttWHERE score_ranking <= 3;+————+———–+——-+—————+| cname | sname | score | score_ranking |+————+———–+——-+—————+| dble | 张三 | 89 | 1 || dble | 欧阳修 | 88 | 2 || dble | 菠菜 | 86 | 3 || mongodb | 潘金莲 | 99 | 1 || mongodb | 欧阳修 | 98 | 2 || mongodb | 郭靖 | 89 | 3 || mysql | 李四 | 100 | 1 || mysql | 潘金莲 | 81 | 2 || mysql | 欧阳修 | 79 | 3 || oracle | 武松 | 98 | 1 || oracle | 李四 | 97 | 2 || oracle | 张三 | 88 | 3 || postgresql | 黄蓉 | 95 | 1 || postgresql | 郭靖 | 90 | 2 || postgresql | 李四 | 82 | 3 |+————+———–+——-+—————+15 rows in set (0.00 sec)
MySQL 8.0 窗口函数
MySQL 8.0 后提供了原生的窗口函数支持,语法和大多数数据库一样,比如还是之前的例子:
用 row_number() over () 直接来检索排名。
mysql>SELECT*FROM(ame,a.sname,c.score,row_number() over (PARTITION BY ameORDER BY c.score DESC) score_rankFROMstudent AS a,course AS b,score AS cWHERE a.sid = c.sidAND b.cid = c.cid) yttWHERE score_rank <= 3;+————+———–+——-+————+| cname | sname | score | score_rank |+————+———–+——-+————+| dble | 张三 | 89 | 1 || dble | 欧阳修 | 88 | 2 || dble | 菠菜 | 86 | 3 || mongodb | 潘金莲 | 99 | 1 || mongodb | 欧阳修 | 98 | 2 || mongodb | 郭靖 | 89 | 3 || mysql | 李四 | 100 | 1 || mysql | 潘金莲 | 81 | 2 || mysql | 欧阳修 | 79 | 3 || oracle | 武松 | 98 | 1 || oracle | 李四 | 97 | 2 || oracle | 张三 | 88 | 3 || postgresql | 黄蓉 | 95 | 1 || postgresql | 郭靖 | 90 | 2 || postgresql | 李四 | 82 | 3 |+————+———–+——-+————+15 rows in set (0.00 sec)
那大家再找出课程 MySQL 和 DBLE 里不及格的倒数前两名学生名单。
mysql>SELECT*FROM(ame,a.sname,c.score,row_number () over (PARTITION BY b.cidORDER BY c.score ASC) score_rankingFROMstudent AS a,course AS b,score AS cWHERE a.sid = c.sidAND b.cid = c.cidAND b.cid IN (, 2001)AND c.score < 60) yttWHERE score_ranking < 3;+——-+————–+——-+—————+| cname | sname | score | score_ranking |+——-+————–+——-+—————+| mysql | 菠菜 | 40 | 1 || mysql | 杨发财 | 42 | 2 || dble | 武松 | 34 | 1 || dble | 东方不败 | 36 | 2 |+——-+————–+——-+—————+4 rows in set (0.00 sec)
到此为止,大家只是演示了row_number() over() 函数的使用方法,其他的函数有兴趣的朋友可以自己体验体验,方法都差不多。
如果觉得《mysql数据库排序规则 如何使用MySQL中的实用函数及查询技巧 – 数据库 – 前端 mysql 分组top》对你有帮助,请点赞、收藏,并留下你的观点哦!