当前位置: 首页 > news >正文

MySQL窗口函数学习

视频链接

基本语法

窗口限定一个范围,它可以理解为满足某些条件的记录集合,窗口函数也就是在窗口范围内执行的函数。
基本语法
窗口函数有over关键字,指定函数执行的范围,可分为三部分:分组子句(partition by),排序子句(order by),窗口子句(rows)
<函数名>over(partition by<分组的列>order by<排序的列>rows between <起始行>and <终止行>)

窗口函数适用于在不破坏原有表结构的基础上,新增一列

窗口的确定

分组子句(partition by)

根据分组设定静态窗口的范围。
不分组可以写成partition by null或者直接不写后面可以跟多个列,如 partition by cid,sname

注意 partition by与group by的区别
1)partition by不会压缩行数但是group by会

2)后者只能选取分组的列和聚合的列
也就是说group by 后生成的结果集与原表的行数和列数都不同

排序子句(order by)

不排序可以写成order by nul 或者直接不写asc或不写表示升序,desc表示降序
后面可以跟多个列,如 order by cid,sname

窗口子句(rows)

窗口子句的描述
1)起始行:N preceding/unbounded preceding
2)当前行:current row
3)终止行:N following/unbounded following
举例:
rows between unbounded preceding and current row 从之前所有的行到当前行
rows between 2 precedingand current row 从前面两行到当前行
rows between current row and unbounded following 从当前行到之后所有的行
rows between current row and 1following 从当前行到后面一行

注意:
排序子句后面缺少窗口子句,窗口规范默认是rows between unbounded preceding and current row
排序子句和窗口子句都缺失,窗口规范默认是 rows between unbounded preceding and unbounded following

总体流程
1)通过partition byorder by 子句确定大窗口(定义出上界unbounded preceding和下界unbounded following)
2) 通过row 子句针对每一行数据确定小窗口(滑动窗口)
3)对每行的小窗口内的数据执行函数并生成新的列

函数的分类

排序类

rank,dense rank,row number

--【排序类】
-- 按班级分组后打上序号不考虑并列
SELECT *, ROW_NUMBER() OVER (PARTITION BY cid ORDER BY score DESC) AS '不可并列排名' FROM SQL_5;

-- 按班级分组后作跳跃排名考虑并列
SELECT *, RANK() OVER (PARTITION BY cid ORDER BY score DESC) AS '跳跃可并列排名' FROM SQL_5;

-- 按班级分组后作连续排名考虑并列
SELECT *, DENSE_RANK() OVER (PARTITION BY cid ORDER BY score DESC) AS '连续可并列排名' FROM SQL_5;

-- 合并起来对比
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 SQL_5;

聚合类

sum, avg, count, max, min

--【聚合类】
-- 让同一班级每个学生都知道班级总分是多少
SELECT *, SUM(score) OVER (PARTITION BY cid) AS '班级总分' FROM SQL_5;
-- 或者可以写成
SELECT *, SUM(score) OVER (PARTITION BY cid ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS '班级总分' FROM SQL_5;

-- 计算同一班级,每个同学和比他分数低的同学的累计总分是多少
SELECT *, SUM(score) OVER (PARTITION BY cid ORDER BY score) AS '累加分数' FROM SQL_5;
-- 或者可以写成其中rows between ... and 是规定窗口大小
SELECT *, SUM(score) OVER (PARTITION BY cid ORDER BY score ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS '班级总分' FROM SQL_5;

跨行类

lag, lead

-- lag/lead 函数参数说明:
参数1:比较的列
参数2:偏移量
参数3:找不到的默认值

-- 同一班级内,成绩比自己低一名的分数是多少
select *, lag(score, 1) over (partition by cid order by score) as '低一名的分数' from SQL_5;
-- 或者写成
select *, lag(score, 1, 0) over(partition by cid order by score) as '低一名的分数' from SQL_5;
-- 向后查询:班级内,成绩比自己高2名的分数是多少
select *, lead(score, 2) over(partition by cid order by score) as '高两名的分数' from SQL_5;

具体问题

分组内TopN查询解决方案

问题1:求出每个学生成绩最高的三条记录

(基于 groupby + order by 实现)

SELECT * FROM (
    SELECT 
        *,
        ROW_NUMBER() OVER (PARTITION BY sname ORDER BY score DESC) AS rn 
    FROM SQL_6
) AS temp
WHERE rn <= 3;

问题2:找出每门课程成绩都高于班级课程平均分的学生

  1. 先求出每个班级、每门课程的平均分
  2. 筛选出所有课程成绩均高于对应班级课程平均分的学生
WITH
-- 1) 计算每个班级每门课程的平均分(t1)
t1 AS (
    SELECT 
        *,
        AVG(score) OVER (PARTITION BY cid, course) AS avg
    FROM SQL_6
),
-- 2) 计算学生成绩与班级平均分的差值(t2)
t2 AS (
    SELECT 
        *,
        score - avg AS 'del'
    FROM t1
)

-- 3) 筛选每门课都高于平均分的学生
SELECT sname 
FROM t2
GROUP BY sname
HAVING MIN(del) > 0;

问题3:求每个部门工资最高的前三名员工

-- 1. 首先对每个部门的员工工资进行排序,然后给出1 2 3 的序号
with t1 as (
	SELECT *, row_number() OVER(partition by dept_no ORDER BY salary DESC) as rn FROM SQL_7
)

--2. 找每个部门的序号rn <= 3的
SELECT * FROM t1
WHERE rn <= 3

-- 合并起来: 
select
	* from (
	select *, row_number() over (partition by dept_no order by salary desc) as rn from SQL_7) as tmp
where rn<= 3;

问题四:计算这些员工的工资占所属部门总工资的百分比

with t1 as(
	select *, sum(salary) over (partition by dept_no) as 'sum_sal' from SQL_7
),
t2 as (select *,round(salary*100/sum_sal,2)as 'percentage' from t1 

select * from t2;

问题五:对各部门员工的工资进行从小到大排序,排名前30%为低层,30%-80%为中层,高于80%为高层,并打上标签

--1)对各部门员工的工资进行从小到大排序并排名select * from SOL_7;
with tl as(
	select *,
		   row_number() over (partition by dept_no order by salary) as rn,
		   count(empno) over (partition by dept_no) as cnt from SQL_7
),
t2 as(
select *,round(rn/cnt,2)as percent from t1


SELECT *, 
	case when percent <= 0.3 then'低层select *
		 when percent <= 0.8 then'中层
		 when percent <= 1 then'高层
	end as label from t2;

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

每年入职总数:

select year(hire_date) as hire_year, 
	   count(empno) as cnt from SQL_7 
group by year(hire_date) 
order by hire_year ;
	   

累计入职总人数:

with tl as(
	select 
		year(hire_date) as hire_year, 
		count(empno) as cntfrom SQL_7
	group by year(hire_date) order by hire_year
)
select *, sum(cnt) over (order by hire_year) as sum from t1;


连续问题

相关文章:

  • [WUSTCTF2020]CV Maker1
  • k8s 自动伸缩的场景与工作原理
  • Docker Desktop - WSL distro terminated abruptly
  • 关于jdk17安装后没有jre目录的解决办法
  • 机器人轨迹跟踪控制——CLF-CBF-QP
  • Redis-基本数据类型
  • 基于VMware的Cent OS Stream 8安装与配置及远程连接软件的介绍
  • 【7】基础入门篇 | YOLOv8 项目【训练】【验证】【推理】最简单教程 | YOLOv8必看 | 最新更新,直接打印 FPS,mAP50,75,95
  • NXP iMX8MP ARM 平台 EMQX 部署测试
  • C++自学笔记---数组和指针的异同点
  • 【11408学习记录】考研英语长难句解析 | 语法拆分+写作模板+真题精讲(附高分秘籍)
  • HTML5图片裁剪工具实现详解
  • LeetCode18四数之和
  • 【Redis】数据的淘汰策略
  • linux磁盘创建分区
  • 开源协议指南:主流许可证核心区别解析
  • 基于javaweb的SpringBoot汉服文化bbs系统设计与实现(源码+文档+部署讲解)
  • “Pseudo Global Warming”:伪全球变暖PGW
  • Diffusion-Based Planning for Autonomous Driving with Flexible Guidance论文细读
  • 【奇点时刻】GPT4o新图像生成模型底层原理深度洞察报告
  • 网站站内搜索怎么做/百度提交入口的注意事项
  • wordpress主动推送到Google/seo建站优化
  • 模板网站开发/私人做网站建设
  • 鹿泉手机网站建设/百度数据中心
  • 宁波象山网站建设/网站seo排名优化价格
  • 太原网站开发团队/北京网站优化体验