首页 > 数据库 >MySQL学习八:窗口函数(一)

MySQL学习八:窗口函数(一)

时间:2024-03-20 21:33:43浏览次数:29  
标签:窗口 函数 over partition 001 score MySQL order select

目录

一、窗口函数

1. 窗口函数定义

窗口限定一个范围,可以理解为满足某些条件的记录集合。窗口函数也就是在窗口范围内的执行的函数

2. 窗口函数语法

窗口函数有 over 关键字,指定函数执行的范围。可分为三部分:分组字句 (partition by),排序字句 (order by),滑动窗口字句 (rows)

<函数名> over (partition by <分组的列名>, order by <排序的列名> rows between <起始行> and <终止行>)

3. 演示表格一

create table gradeInfo1 (
    cid varchar(32),
    sname varchar(32),
    score int
);

insert into gradeInfo1 (cid, sname, score)
values
('001', '张三', 78),
('001', '李四', 82),
('002', '小明', 90),
('001', '王五', 67),
('002', '小红', 85),
('002', '小刚', 90);
cid(班级id)sname (学生姓名)score(分数)
001张三78
001李四82
002小明90
001王五67
002小红85
002小刚90

4. 窗口的确定

4.1 例1:查询各班级总分

# 方法一
select *,
sum(score) over(partition by cid) as 班级总分
from gradeinfo1

# 方法二
select *, 
sum(score) over(partition by cid order by score rows between unbounded preceding and unbounded following) as 班级总分
from gradeinfo1

在这里插入图片描述

4.2 例2:查询各班级累计总分

# 方法一
select *,
sum(score) over(partition by cid order by score) as 班级总分
from gradeinfo1

# 方法二
select *,
sum(score) over(partition by cid order by score rows between unbounded preceding and current row) as 班级总分
from gradeinfo1

在这里插入图片描述

4.3 分区子句(partition by)

① 不分区可以写成 partition by null,或者直接不写
② 后面可以跟多个列,例如 partition by cid, sname
partition by 和 group by的区别: partition by 不将行数去重,group by 会将行数去重

4.4 排序子句(order by)

① 不排序可以写成 order by null,或者直接不写
② 后面可以跟多个列,例如 order by cid, sname

4.5 窗口子句(rows)

语法概述:
① 起始行:N preceding / unbounded preceding
② 当前行:current row
③ 终止行:N following / unbounded following
举例:
① 从分区内的前面所有行到当前行:rows between unbounded preceding and current row
② 从分区内的前面 2 行 到当前行:rows between 2 preceding and current row
③ 从分区内的当前行到后面所有行:rows between current row and unbounded following
④ 从分区内的当前行到后面 3 行:rows between current row and 3 following
注意:
① 若排序字句后缺少窗口字句,窗口规范默认为:rows between unbounded preceding and current row
② 若排序字句和窗口字句都缺少,窗口规范默认为:rows between unbounded preceding and unbounded following

4.6 总体执行流程

① 通过 partition by 个 order by 确定大窗口(定义出上界和下界)
② 通过 rows 字句针对每一行数据,确定小窗口(即滑动窗口)
③ 针对每行的小窗口内的数据,执行函数并生成新的列

5. 函数分类

5.1 排序类函数

① row_number:序号不重复,且序号连续(即连续排名,不考虑并列)。例如:1,2,3 …
② rank:序号可重复,且序号不连续(即跳跃排名,考虑并列)。例如:1,2,2,4 …
③ dense_rank:序号可重复,且序号连续(即连续排名,考虑并列)。例如:1,2,2,3 …

# 对每个班级的学生成绩进行排名
select *,
row_number() over(partition by cid order by score desc) as 不并列排名,
rank() over(partition by cid order by score desc) as 跳跃可并列排名,
dense_rank() over(partition by cid order by score desc) as 连续可并列排名
from gradeinfo1

在这里插入图片描述

5.2 聚合类函数

sum,avg,count,max,min

5.3 跨行类函数

① lead (比较的列,N):返回当前行的后面 N 行
② lag (比较的列,N):返回当前行的前面 N 行

# 每个班级中,成绩比自己高一名的成绩是多少
select *,
lag(score, 1) over(partition by cid order by score desc) as 高一名的分数
from gradeinfo1

在这里插入图片描述

二、相关题目练习

2.1 演示表格二

create table gradeInfo2 (
    cid varchar(32),
    sname varchar(32),
    course varchar(32),
    score int
);

insert into gradeInfo2 (cid, sname, course, score)
values
('001', '张三', '语文', 78),
('002', '小刚', '语文', 71),
('001', '李四', '数学', 56),
('001', '王五', '数学', 97),
('002', '小明', '数学', 54),
('002', '小刚', '数学', 67),
('002', '小红', '数学', 82),
('001', '王五', '语文', 80),
('001', '张三', '数学', 77),
('002', '小明', '语文', 58),
('002', '小红', '语文', 87),
('001', '李四', '语文', 60),
('001', '张三', '英语', 66),
('002', '小刚', '英语', 50),
('001', '李四', '地理', 59),
('001', '王五', '地理', 88),
('002', '小明', '地理', 45),
('002', '小刚', '地理', 66),
('002', '小红', '地理', 82),
('001', '王五', '英语', 81),
('001', '张三', '地理', 77),
('002', '小明', '英语', 55),
('002', '小红', '英语', 87),
('001', '李四', '英语', 61)
cid(班级id)sname (学生姓名)course(学科)score(分数)
001张三语文78
002小刚语文71
001李四数学56
001王五数学97
002小明数学54

题目一:查询每个学生成绩最高的三个学科(分组内 Top N 问题)

select *
from(
	select *,
	row_number() over(partition by sname order by score desc) as ranking
	from gradeinfo2
) a
where ranking <= 3

在这里插入图片描述

题目二:查询每个学科都高于班级学科平均分的学生(汇总分析问题)

select sname
from(
	select *, score - course_avg_score as del
    from(
    	select *,
		avg(score) over(partition by cid, course) as course_avg_score
		from gradeinfo2
    ) t1
) t2
group by sname
having min(del) > 0 

在这里插入图片描述

2.2 演示表格三

create table employee(
		empno varchar(32),
        ename varchar(32),
        hire_date varchar(32),
        salary int,
        dept_no varchar(32)
);

insert into employee(empno, ename, hire_date, salary, dept_no)
values
('001', 'Adam', '2018-03-01', 1000, 'A'),
('002', 'Bill', '2021-03-01', 1200, 'A'),
('003', 'Cindy', '2016-03-01', 1500, 'A'),
('004', 'Danney', '2020-03-01', 5000, 'A'),
('005', 'Eason', '2020-03-01', 4000, 'B'),
('006', 'Fred', '2018-03-01', 3500, 'B'),
('007', 'Gary', '2017-03-01', 1800, 'B'),
('008', 'Hugo', '2020-03-01', 4500, 'B')
empnoenamehire_datesalarydept_no
001Adam2018-03-011000A
002Bill2021-03-011200A
003Cindy2016-03-011500A
004Danney2020-03-015000A
005Eason2020-03-014000B
006Fred2018-03-013500B
007Gary2017-03-011800B
008Hugo2020-03-014500B

题目一:查询每个部门工资最高的前三个员工信息(分组内 Top N 问题)

select *
from(
	select *,
	rank() over(partition by dept_no order by salary desc) as ranking
	from employee
) a
where ranking <= 3

在这里插入图片描述

题目二:查询员工工资占所属部门总工资的百分比(汇总分析问题)

select *, 
concat(round(salary/total_salary*100, 3), '%') as salary_percent
from(
	select *,
	sum(salary) over(partition by dept_no) as total_salary
	from employee
) a

在这里插入图片描述

题目三:对各部门员工的工资进行升序排列,排名前 30% 为底层,30% - 80% 为中层,80% 以上为高层,并打上标签

select *,
case
	when ranking_percent < 0.3 then '底层'
	when ranking_percent between 0.3 and 0.8 then '中层'
	else '高层'
	end as label
from(
	select *, round(ranking/total_employee, 2) as ranking_percent
	from(
		select *,
		rank() over(partition by dept_no order by salary) as ranking,
		count(empno) over(partition by dept_no) as total_employee
		from employee
	) a
) b

在这里插入图片描述

题目四:查询每年入职总数以及截至本年累计入职总人数(本年总入职人数 + 本年之前所有年的入职人数之和)

# 方法一
# 创建临时表
with
a as(
	select year(hire_date) as 入职年份, count(empno) as 入职员工数 from employee
	group by 入职年份
	order by 入职年份
)

select *,
sum(入职员工数) over(rows between unbounded preceding and current row) as 累计入职总人数
from a


# 方法二
select *,
sum(入职员工数) over(rows between unbounded preceding and current row) as 累计入职总人数
from(
	select year(hire_date) as 入职年份, count(empno) as 入职员工数 from employee
	group by 入职年份
	order by 入职年份
) a

在这里插入图片描述

六、窗口函数问题与技巧总结

① 本文用窗口函数解决的 2 个主要问题:分区内 Top N 问题,汇总分析问题

② 分区内 Top N 公式

select *
from(
	select *,
	row_number() over(partition by 分区列 order by 比较列) as ranking
	from gradeinfo2
) a
where ranking <= N

③ 窗口函数 → 生成辅助列(用于计算百分比等)

④ 重要:with 子句 → 生成临时表,把复杂的问题拆分成多个子问题,再用临时表表达

标签:窗口,函数,over,partition,001,score,MySQL,order,select
From: https://blog.csdn.net/2301_79898146/article/details/136868236

相关文章

  • 了解和编译安装mysql数据库
    一.数据库的基本概念1.1数据①描述事物的符号记录称为数据(Data)。包括数字、文字、图形、图像、声音、档案记录等②数据是以“记录”的形式按照统一的格式进行存储的,而不是杂乱无章的。相同格式和类型的数据统一存放在一起,而不会把“人”和“书”混在一起存储。这样,数......
  • MySQL | CRUD
    目录1.Create2.Retrieve2.1.SELECT列2.1.1.全列查询2.1.2.指定列查询2.1.3.查询字段为表达式2.1.4.为查询结果指定别名2.1.5.结果去重2.2.WHERE条件2.2.1.年龄小于19的同学2.2.2.id在2~3的同学2.2.3.id为1和4的同学2.2.4.姓张的同学及张某的同学2.3......
  • 记一下mysql隔离级别问题
      MSQL默认隔离级别是  可重复读; 可重复读即 同一次查询,再次查询结果一致;不会查询到别的事务提交的内容;原理:开始事务后,做一次select产生一个readview,这个readview已经确定了能读取的undolog链;简单理解就是只能读取到当前事务版本之前的数据;当另一个事务插入数据......
  • MFC 动态拆分窗口
    MFC动态拆分窗口,使用MFC自带的CSplitterWnd类进行操作。其步骤就是:1、在框架类添加此类型的成员函数。2、覆盖OnCreateClent函数。3、调用create函数。BOOLCChildFrame::OnCreateClient(LPCREATESTRUCTlpcs,CCreateContext*pContext){//TODO:在此添加专用代码......
  • MySQL中如何快速定位占用CPU过高的SQL
    作为DBA工作中都会遇到过数据库服务器CPU飙升的场景,我们该如何快速定位问题?又该如何快速找到具体是哪个SQL引发的CPU异常呢?下面我们说两个方法。聊聊MySQL中如何快速定位占用CPU过高的SQL。技术人人都可以磨炼,但处理问题的思路和角度各有不同,希望这篇文章可以抛砖引玉。 以一......
  • 【数据库】MySQL表的增删改查(二)
    文章目录一、数据库约束一、数据库约束1.1约束类型NOTNULL-指示某列不能存储NULL值。UNIQUE-保证某列的每行必须有唯一的值。DEFAULT-规定没有给列赋值时的默认值。PRIMARYKEY-NOTNULL和UNIQUE的结合。确保某列(或两个列多个列的结合)有唯一标识......
  • 在sort中传入仿函数
    仿函数就是用来控制排列顺序的map<int,int,Compare>是这样,list.sort()也是这样.//List双向链表.cpp:此文件包含"main"函数。程序执行将在此处开始并结束。//#include<iostream>#include<list>usingnamespacestd;structCompare{ booloperator()(constint&......
  • 你真的了解c语言的10大字符串函数吗?
    乐观学习,乐观生活,才能不断前进啊!!!我的主页:optimistic_chen我的专栏:c语言点击主页:optimistic_chen和专栏:c语言,创作不易,大佬们点赞鼓励下吧~前言:长度不受限制的字符串函数-strcpy,strcat,strcmp长度受限制的字符串函数-strncpy,strncat,strncmp文章目录前言:1.st......
  • 字符串函数
    这些字符串函数一定加头文件#include<string.h>strlen函数:1.它的功能:计算字符串的长度 2.strlen的实现:intmy_strlen(constchar*str){intcount=0;assert(str);while(*str){count++;str++;}returncount;}strcut函数:1.这个函数实现的是字......
  • SQL窗口函数
    通俗易懂的学会:SQL窗口函数-知乎(zhihu.com)SQL题目讲解——窗口函数(一)_哔哩哔哩_bilibili因为窗口函数是对where或者groupby子句处理后的结果进行操作,所以窗口函数原则上只能写在select子句中  ......