当查询条件需要用到复杂子查询时,聚合函数操作起来非常麻烦,因此使用开窗函数能够轻松实现。

注意:在Oracle中称为分析函数。

在MySQL中称为开窗函数,使用于MySQL8.0以上版本,sql sever、hive、Oracle等。

1 开窗函数

开窗函数:为将要被操作的行的集合定义一个窗口,它对一组值进行操作,不需要使用GROUP BY子句对数据进行分组,能够在同一行中同时返回基础行的列和聚合列。

语法:函数 + over(partition by <分组用列> order by <排序用列>)

over() 按所有行进行分组

over(partition by xxx)按xxx分组的所有行进行分组

over(partition by xxx order by aaa)按列xxx分组,按列aaa排序

over(order by aaa) 按aaa列排序

括号中的两个关键词partition by 和order by可以只出现一个。

开窗函数表示对数据集按照分组用列进行分区,并且对每个分区按照函数聚合计算,最终将计算结果按照排序用列排序后返回到该行。

无论何种能力,窗口函数都不会影响数据行数,而是将计算平摊在每一行。

1.1 开窗函数分类

名称 描述
cume_dist() 计算一组值中一个值的累积分布
dense_rank() 根据该order by子句为分区中的每一行分配一个等级。它将相同的等级分配给具有相等值的行。如果两行或更多行具有相同的排名,则排名值序列中将没有间隙
first_value() 返回相对于窗口框架第一行的指定表达式的值
lag() 返回分区中当前行之前的第N行的值。如果不存在前一行,则返回NULL
last_value() 返回相对于窗口框架中最后一行的指定表达式的值
lead() 返回分区中当前行之后的第N行的值。如果不存在后续行,则返回NULL
nth_value() 从窗口框架的第N行返回参数的值
ntile() 将每个窗口分区的行分配到指定数量的排名组中
percent_rank() 计算分区域结果集中行的百分数等级
rank() 与dense_rank()函数相似,不同之处在于当两行或更多行具有相同的等级时,等级值序列中存在间隙
row_number() 为分区中的每一行分配一个顺序整数

按照函数功能不同,MySQL支出的开窗函数分为如下几类:

  • 序号函数:row_number() / rank() / dense_rank()
  • 分布函数:percent_rank() / cume_dist()
  • 前后函数:lag() / lead()
  • 头尾函数:first_value() / last_value()
  • 其他函数 :nth_value() / nfile()

2 排序开窗函数和聚合开窗函数

2.1 排序开窗函数

  • row_number(行号)
  • rank(排名)
  • dense_rank(密集排名)
  • ntile(分组排名)

例1 先对所有数据进行排序

select s.sid,s1.sname,s1.gender,c.cname,s.num,   row_number() over
(partition by c.cname order by num desc) as row_number排名,
rank() over (partition by c.cname order by num desc) as rank排名,
dense_rank() over (partition by c.cname order by num desc) as dense_rank排名,
ntile(6) over (partition by c.cname order by num desc) as ntile排名
from score s   join student s1 on s.student_id = s1.sid
left join course c on s.course_id = c.cid

结果如下:

 row_number

根据课程进行分组,然后对每组内的成绩进行降序排序

又上图可知row_number对于同组内的相同成绩并没有做特殊处理,而仅仅是生成连续的序号。row_number常用于按照某列生成连续序号。

rank

rank函数就是对查询出来的记录进行排名。

与row_number函数不同的是,rank函数考虑到了over子句中排序字段值相同的情况,over子句中排序字段值相同的序号是一样的,后面字段值不相同的序号将跳过相同的排名号排下一个,也就是相关行之前的排名数加一,通过上面的例子我们也可以看出,rank考虑到值相同情况,并且它的排名存在跳跃性。

dense_rank

密集排名,在考虑了值相同时,排名也相同,但是序号不跳跃,紧跟上一个序号。

例如题目中体育成绩有2位同学(张三和刘三)并列第一,如果使用rank排名 ,那钢蛋就是第三名,而如果采用dense_rank 那钢蛋就是第二名,这个很容易理解吧。

ntile

ntile会先根据你的分组依据,本题中是课程名称,然后把每个组的总记录数进行按照你给的ntile()里的数字进行,这个数字就是桶数,相当于是把体育课程总共12条记录,尽量等划分成5桶,然后按照num的排序等级划分,每个桶两条记录,也就是112233445566的排序结果了,很显然,这个排序结果的数字大小只能用于桶与桶之间,而桶内部记录虽然序号相同,但是num不一定相同。

例如:统计各科成绩前三

select * from
(select s.sid,s1.sname,s1.gender,c.cname,s.num,dense_rank()
over (partition by c.cname order by num desc) as dense_rank排名 from score s
join student s1 on s.student_id = s1.sid
left join course c on s.course_id = c.cid) as e
where dense_rank排名 <= 3;

2.2 聚合开窗函数

函数名如果是聚合函数,则称为聚合开窗函数

语法:聚合函数(列) over(partition by 列 order by 列)

常见的聚合函数有:sum()、count()、average()、max()、min()

计算每个学生的及格科目数

select student_id,count(sid) from  score where num>= 60 group by student_id;

通过普通的聚合函数分组计算后,数据表结构发生了变化,它会根据分组进行显示,并且,如果你是根据学生ID分组,那你查询的字段应该也是学生ID,不然会影响到分组结果所对应的数值,例如现在查询条件在添加一个Sid

select sid,student_id,count(sid) from  score where num>= 60 group by student_id;

sid的数据并没有实际意义,因为数据表已经根据分组发生了变化。

执行结果

select sid,student_id,count(sid) over(PARTITION by student_id orderby student_id) 及格数   from score where num>= 60;

总结:开窗函数不会修改源数据表的结果,也是在表的最后一列添加想要的结果。

实例数据

CREATE TABLE class (cid int(11) NOT NULL AUTO_INCREMENT,caption varchar(32) NOT NULL,PRIMARY KEY (cid)
) ENGINE=InnoDB CHARSET=utf8;INSERT INTO class VALUES
(1, '三年二班'),
(2, '三年三班'),
(3, '一年二班'),
(4, '二年九班');CREATE TABLE teacher(tid int(11) NOT NULL AUTO_INCREMENT,tname varchar(32) NOT NULL,PRIMARY KEY (tid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO teacher VALUES
(1, '张磊老师'),
(2, '李平老师'),
(3, '刘海燕老师'),
(4, '朱云海老师'),
(5, '李杰老师');CREATE TABLE course(cid int(11) NOT NULL AUTO_INCREMENT,cname varchar(32) NOT NULL,teacher_id int(11) NOT NULL,PRIMARY KEY (cid),KEY fk_course_teacher (teacher_id),CONSTRAINT fk_course_teacher FOREIGN KEY (teacher_id) REFERENCES teacher (tid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO course VALUES
(1, '生物', 1),
(2, '物理', 2),
(3, '体育', 3),
(4, '美术', 2);CREATE TABLE student(sid int(11) NOT NULL AUTO_INCREMENT,gender char(1) NOT NULL,class_id int(11) NOT NULL,sname varchar(32) NOT NULL,PRIMARY KEY (sid),KEY fk_class (class_id),CONSTRAINT fk_class FOREIGN KEY (class_id) REFERENCES class (cid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO student VALUES
(1, '男', 1, '理解'),
(2, '女', 1, '钢蛋'),
(3, '男', 1, '张三'),
(4, '男', 1, '张一'),
(5, '女', 1, '张二'),
(6, '男', 1, '张四'),
(7, '女', 2, '铁锤'),
(8, '男', 2, '李三'),
(9, '男', 2, '李一'),
(10, '女', 2, '李二'),
(11, '男', 2, '李四'),
(12, '女', 3, '如花'),
(13, '男', 3, '刘三'),
(14, '男', 3, '刘一'),
(15, '女', 3, '刘二'),
(16, '男', 3, '刘四');CREATE TABLE score (sid int(11) NOT NULL AUTO_INCREMENT,student_id int(11) NOT NULL,course_id int(11) NOT NULL,num int(11) NOT NULL,PRIMARY KEY (sid),KEY fk_score_student (student_id),KEY fk_score_course (course_id),CONSTRAINT fk_score_course FOREIGN KEY (course_id) REFERENCES course (cid),CONSTRAINT fk_score_student FOREIGN KEY (student_id) REFERENCES student(sid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;INSERT INTO score VALUES
(1, 1, 1, 10),
(2, 1, 2, 9),
(5, 1, 4, 66),
(6, 2, 1, 8),
(8, 2, 3, 68),
(9, 2, 4, 99),
(10, 3, 1, 77),
(11, 3, 2, 66),
(12, 3, 3, 87),
(13, 3, 4, 99),
(14, 4, 1, 79),
(15, 4, 2, 11),
(16, 4, 3, 67),
(17, 4, 4, 100),
(18, 5, 1, 79),
(19, 5, 2, 11),
(20, 5, 3, 67),
(21, 5, 4, 100),
(22, 6, 1, 9),
(23, 6, 2, 100),
(24, 6, 3, 67),
(25, 6, 4, 100),
(26, 7, 1, 9),
(27, 7, 2, 100),
(28, 7, 3, 67),
(29, 7, 4, 88),
(30, 8, 1, 9),
(31, 8, 2, 100),
(32, 8, 3, 67),
(33, 8, 4, 88),
(34, 9, 1, 91),
(35, 9, 2, 88),
(36, 9, 3, 67),
(37, 9, 4, 22),
(38, 10, 1, 90),
(39, 10, 2, 77),
(40, 10, 3, 43),
(41, 10, 4, 87),
(42, 11, 1, 90),
(43, 11, 2, 77),
(44, 11, 3, 43),
(45, 11, 4, 87),
(46, 12, 1, 90),
(47, 12, 2, 77),
(48, 12, 3, 43),
(49, 12, 4, 87),
(52, 13, 3, 87);

MySQL:开窗函数相关推荐

  1. MySQL——开窗函数

    结合order by关键词和limit关键词是可以解决很多的topN问题,比如从二手房数据集中查询出某个地区的最贵的10套房,从电商交易数据集中查询出实付金额最高的5笔交易,从学员信息表中查询出年龄最 ...

  2. mysql开窗函数over_sql中的开窗函数over()

    今天刷LeetCode的时候看到一道题,题目是这个样子 LeetCode上面要求是用mysql来解决这道题,因为平时我上班的时候大部分时间都是在sqlserver上操作,所以一看到这个题目的要求我脑海 ...

  3. mysql开窗函数_魔幻的SQL开窗函数,为您打开进阶高手的一扇天窗

    经常写SQL脚本的朋友,通常会有一种迷之自信,似乎各种问题都有自己的一套解决方案.时间长了,人的思维可能会逐渐固化.思维固化能提高工作效率,但从某些角度看是很可怕的,我们也同时会失去接受新知识的内在动 ...

  4. mysql开窗函数有哪些_mysql开窗函数

    开窗函数: 它可以理解为记录集合,开窗函数也就是在满足某种条件的记录集合上执行的特殊函数. 对于每条记录都要在此窗口内执行函数,有的函数随着记录不同,窗口大小都是固定的,这种属于静态窗口: 有的函数则 ...

  5. mysql 开窗函数 累加_开窗函数在MySql中的使用

    MySql在8.0的版本增加了对开窗函数的支持,终于可以在MySql使用开窗函数了.开窗函数又称OLAP函数(Online Analytical Processing).开窗函数的语法结构: #Key ...

  6. MYSQL开窗函数详解

    基本概念 MYSQL8.0支持窗口函数(Window Function),也称分析函数.窗口函数与组分聚合函数类似,但是每一行数据都会生成一个结果.如果我们将mysql与pandas中的DataFra ...

  7. mysql开窗函数over_oracle分析函数技术详解(配上开窗函数over())

    一.Oracle分析函数入门 分析函数是什么? 分析函数是Oracle专门用于 解决复杂报表统计需求 的功能强大的函数, 它可以在数据中进行分组然后计算基于组的某种统计 ,并且每一组的每一行都可以返回 ...

  8. mysql为何不支持开窗函数?

    引用 在开窗函数出现之前存在着非常多用 SQL 语句非常难解决的问题,非常多都要通过复杂的相关子查询或者存储过程来完毕.为了解决这些问题,在2003年ISO SQL标准增加了开窗函数,开窗函数的使用使 ...

  9. 【PostgreSQL】使用开窗函数获取历史表最新记录 及 MySQL 旧版本实现

    文章目录 1. 前言 2. 需求与实现 2.1 实现说明 2.2 ROW_NUMBER () 的限制 2.2.1 RANK() 实现 2.2.2 RANK() 的限制 2.2.3 DENSE_RANK ...

最新文章

  1. 汇编call指令详解_我也能写出雷军的的代码吗?最好的汇编语言入门教程在这里!...
  2. bindService初步了解
  3. 【问题记录】python的py文件生成exe可执行程序闪退
  4. Web SQL Database 之 SQLite语法
  5. JavaFX之TableView的MenuButton
  6. java下标运算符_《Java从小白到大牛精简版》之第6章 运算符(下)
  7. 竞价这种方式适合不适合GPU云服务器,怎么买更划算??
  8. 2048+html源码之家,前端纯原生代码实现2048
  9. 计算机病毒相关参考文献,计算机病毒参考文献
  10. 概要、详细设计文档内容简述
  11. html 中添加提示,如何interpretHTML UIB-提示
  12. 向 AppStore iOS 苹果appstore 提交新版本app出现问题
  13. flowable审批流+集成springboot
  14. STM32 USB_SPI_FLASH 简易U盘设计
  15. 杨百翰大学计算机科学专业,杨百翰大学计算机科学硕士.pdf
  16. 数学公式div是什么意思
  17. 《linux内核中断》之 法外狂徒张三删库跑路
  18. java线程状态——java线程状态图
  19. windows远程控制mac的步骤
  20. 计算机与资源管理器有何区别,资源管理器与我的电脑有什么不同.PPT

热门文章

  1. 浅析Firefox 4的改进和对3G前端开发的影响
  2. requests爬虫实践之安居客二手房屋数据(python实现)
  3. linux 命令行下wlan无线网卡配置 2
  4. 马斯克身价接近450亿美元,超过马云仍低于马化腾
  5. 操作系统原理_田丽华(11)大容量存储
  6. 【成为博客专家】大数据面试题
  7. smarty中定义变量
  8. 三国志群英会java,八方群英会《三国志·战略版》盟主齐聚洛阳城 2021由此起航...
  9. 结对项目-最长单词链博客
  10. GKCTF EZ三剑客-EzWeb 题目分析总结