详解MySQL|你了解MySQL 8.0 窗口函数吗?
sinye56 2024-09-22 08:26 5 浏览 0 评论
背景
一直以来,MySQL 只有针对聚合函数的汇总类功能,比如MAX, AVG 等,没有从 SQL 层针对聚合类每组展开处理的功能。不过 MySQL 开放了 UDF 接口,可以用 C 来自己写UDF,这个就增加了功能行难度。
这种针对每组展开处理的功能就叫窗口函数,有的数据库叫分析函数。
在 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 |+-----------+--------------+| 201910001 | 张三 || 201910002 | 李四 || 201910003 | 武松 || 201910004 | 潘金莲 || 201910005 | 菠菜 || 201910006 | 杨发财 || 201910007 | 欧阳修 || 201910008 | 郭靖 || 201910009 | 黄蓉 || 201910010 | 东方不败 |+-----------+--------------+10 rows in set (0.00 sec)
mysql> select * from score;;+-----------+----------+-------+| sid | cid | score |+-----------+----------+-------+| 201910001 | 20192001 | 50 || 201910001 | 20192002 | 88 || 201910001 | 20192003 | 54 || 201910001 | 20192004 | 43 || 201910001 | 20192005 | 89 || 201910002 | 20192001 | 79 || 201910002 | 20192002 | 97 || 201910002 | 20192003 | 82 || 201910002 | 20192004 | 85 || 201910002 | 20192005 | 80 || 201910003 | 20192001 | 48 || 201910003 | 20192002 | 98 || 201910003 | 20192003 | 47 || 201910003 | 20192004 | 41 || 201910003 | 20192005 | 34 || 201910004 | 20192001 | 81 || 201910004 | 20192002 | 69 || 201910004 | 20192003 | 67 || 201910004 | 20192004 | 99 || 201910004 | 20192005 | 61 || 201910005 | 20192001 | 40 || 201910005 | 20192002 | 52 || 201910005 | 20192003 | 39 || 201910005 | 20192004 | 74 || 201910005 | 20192005 | 86 || 201910006 | 20192001 | 42 || 201910006 | 20192002 | 52 || 201910006 | 20192003 | 36 || 201910006 | 20192004 | 58 || 201910006 | 20192005 | 84 || 201910007 | 20192001 | 79 || 201910007 | 20192002 | 43 || 201910007 | 20192003 | 79 || 201910007 | 20192004 | 98 || 201910007 | 20192005 | 88 || 201910008 | 20192001 | 45 || 201910008 | 20192002 | 65 || 201910008 | 20192003 | 90 || 201910008 | 20192004 | 89 || 201910008 | 20192005 | 74 || 201910009 | 20192001 | 73 || 201910009 | 20192002 | 42 || 201910009 | 20192003 | 95 || 201910009 | 20192004 | 46 || 201910009 | 20192005 | 45 || 201910010 | 20192001 | 58 || 201910010 | 20192002 | 52 || 201910010 | 20192003 | 55 || 201910010 | 20192004 | 87 || 201910010 | 20192005 | 36 |+-----------+----------+-------+50 rows in set (0.00 sec)
mysql> select * from course;+----------+------------+| cid | cname |+----------+------------+| 20192001 | mysql || 20192002 | oracle || 20192003 | postgresql || 20192004 | mongodb || 20192005 | dble |+----------+------------+5 rows in set (0.00 sec)
MySQL 8.0 之前
比如我们求成绩排名前三的学生排名,我来举个用 session 变量和 group_concat 函数来分别实现的例子:
session 变量方式
每组开始赋一个初始值序号和初始分组字段。
SELECT b.cname, a.sname, c.score, c.ranking_scoreFROM student a, course b, ( SELECT c.*, IF( @cid = c.cid, @rn := @rn + 1, @rn := 1 ) AS ranking_score, @cid := c.cid AS tmpcid FROM ( SELECT * FROM score ORDER 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 b.cname,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 ( SELECT b.cname, a.sname, c.score, FIND_IN_SET(c.score, d.gp) score_ranking FROM student a, course b, score c, ( SELECT cid, GROUP_CONCAT( score ORDER BY score DESC SEPARATOR ',' ) gp FROM score GROUP BY cid ORDER BY score DESC ) d WHERE a.sid = c.sid AND b.cid = c.cid AND c.cid = d.cid ORDER 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 ( SELECT b.cname, a.sname, c.score, row_number() over ( PARTITION BY b.cname ORDER BY c.score DESC ) score_rank FROM student AS a, course AS b, score AS c WHERE a.sid = c.sid AND 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 ( SELECT b.cname, a.sname, c.score, row_number () over ( PARTITION BY b.cid ORDER BY c.score ASC ) score_ranking FROM student AS a, course AS b, score AS c WHERE a.sid = c.sid AND b.cid = c.cid AND b.cid IN (20192005, 20192001) 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() 函数的使用方法,其他的函数有兴趣的朋友可以自己体验体验,方法都差不多。
相关推荐
- RHEL8和CentOS8怎么重启网络
-
本文主要讲解如何重启RHEL8或者CentOS8网络以及如何解决RHEL8和CentOS8系统的网络管理服务报错,当我们安装好RHEL8或者CentOS8,重启启动网络时,会出现以下报错:...
- Linux 内、外网双网卡路由配置
-
1.路由信息的影响Linux系统中如果有多张网卡的情况下,如果路由信息配置不正确,...
- Linux——centos7修改网卡名
-
修改网卡名这个操作可能平时用不太上,可作为了解。修改网卡默认名从ens33改成eth01.首先修改网卡配置文件名(建议将原配置文件进行备份)...
- CentOS7下修改网卡名称为ethX的操作方法
-
?Linux操作系统的网卡设备的传统命名方式是eth0、eth1、eth2等,而CentOS7提供了不同的命名规则,默认是基于固件、拓扑、位置信息来分配。这样做的优点是命名全自动的、可预知的...
- Linux 网卡名称enss33修改为eth0
-
一、CentOS修改/etc/sysconfig/grub文件(修改前先备份)为GRUB_CMDLINE_LINUX变量增加2个参数(net.ifnames=0biosdevname=0),修改完成...
- CentOS下双网卡绑定,实现带宽飞速
-
方式一1.新建/etc/sysconfig/network-scripts/ifcfg-bond0文件DEVICE=bond0IPADDR=191.3.60.1NETMASK=255.255.2...
- linux 双网卡双网段设置路由转发
-
背景网络情况linux双网卡:网卡A(ens3)和网卡B(...
- Linux-VMware设置网卡保持激活
-
Linux系统只有在激活网卡的状态下才能去连接网络,进行网络通讯。修改配置文件(永久激活网卡)...
- VMware虚拟机三种网络模式
-
01.VMware虚拟机三种网络模式由于linux目前很热门,越来越多的人在学习linux,但是买一台服务放家里来学习,实在是很浪费。那么如何解决这个问题?虚拟机软件是很好的选择,常用的虚拟机软件有v...
- 2023年最新版 linux克隆虚拟机 解决网卡uuid重复问题
-
问题描述1、克隆了虚拟机,两台虚拟机里面的ip以及网卡的uuid都是一样的2、ip好改,但是uuid如何改呢?解决问题1、每台主机应该保证网卡的UUID是唯一的,避免后面网络通信有问题...
- Linux网卡的Vlan配置,你可能不了解的玩法
-
如果服务器上连的交换机端口已经预先设置了TRUNK,并允许特定的VLAN可以通过,那么服务器的网卡在配置时就必须指定所属的VLAN,否则就不通了,这种情形在虚拟化部署时较常见。例如在一个办公环境中,办...
- Centos7 网卡绑定
-
1、切换到指定目录#备份网卡数据cd/etc/sysconfig/network-scriptscpifcfg-enp5s0f0ifcfg-enp5s0f0.bak...
- Linux搭建nginx+keepalived 高可用(主备+双主模式)
-
一:keepalived简介反向代理及负载均衡参考:...
- Linux下Route 路由指令使用详解
-
linuxroute命令用于显示和操作IP路由表。要实现两个不同子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。在Linux系统中,设置路由通常是为了解决以下问题:该...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- linux进程通信 (63)