窗口函数
窗口函数的引入是为了解决想要显示聚集前的数据,又要显示聚集后的数据;窗口数对一组值进行操作,不需要使用group by子句对数据进行分组,能够在同一行中同时返回基础行的列和聚合列。
基本语法:
函数名(列) over(选项) 选项为partition by 列 order by 列
解释:
over(partition by XXX) 将所有行按XXX进行分组
over(partition by XXX order by aaa) 按XXX分组,按aaa排序
注意:
聚合函数类似数据透视表,原有表结构已发生变化,
窗口函数不会改变原表结构,
聚合窗口函数
需求:计算每个学生的及格科目数
--使用聚合函数
select student_id,count(sid) from score where num>=60 group by student_id;
--使用窗口函数
select student_id,count(sid) over(partition by student_id order by student_id) from score where num>=60;
排序窗口函数
1、row_number()
---仅仅根据行号进行排序,相同结果则排序按照顺序依次排
2、rank()
---排名,与row_number()不同的是,rank函数考虑到了over子句中排序字段值相同的情况,over子句中排序字段值相同的排序结果是一样的,例如:11335
3、dense_rank()
---密集排名,与rank类似,唯一不同的是当排序结果相同时,它的排序不跳跃,而是紧跟排下一个。例如:11223
4、ntile(n)
---桶排名,常用于提取前百分之多少的应用场景。
首先,ntile会先根据分组依据,然后把每个组的总记录数按照n进行均分,这个数字就是桶数;
例如,一个组内共12条记录,若n=6,则等划分为6桶,然后按照num的排序等级划分,12/6=2,也就是112233445566。
//计算每门课程前三,考虑排名相同的情况
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 a where dense_rank<=3;
位置移动窗口函数
1、lag(col,n):用于统计窗口内往上第n行值
2、lead(col,n):用于统计窗口内往下第n行值
---计算作弊次数,如果相邻登陆时间小于两分钟即认为作弊:
首先:将相邻两次登录时间使用两个字段存储
select uid,login_time,lead(login_time,1) over(partition by uid order by login_time) lead_time from lag_table;
第二步:
其他窗口函数
1、first_value()
//查询每个课程的第一名成绩
SELECT
s.sid,
s1.sname,
s1.gender,
c.cname,
s.num,
first_value (num) over (
PARTITION BY c.cname
ORDER BY
num DESC
) AS first_value用法
FROM
score s
JOIN student s1 ON s.student_id = s1.sid
LEFT JOIN course c ON s.course_id = c.cid;
2、last_value()
//取每门课程的最后一名成绩,这样写为什么不对呢
SELECT
s.sid,
s1.sname,
s1.gender,
c.cname,
s.num,
last_value (num) over (
PARTITION BY c.cname
ORDER BY
num DESC
) AS last_value用法
FROM
score s
JOIN student s1 ON s.student_id = s1.sid
LEFT JOIN course c ON s.course_id = c.cid;
窗口函数默认统计范围是rows between unbounded preceding and current now,也就是取当前行数据与当前行之前的数据的比较。
在order by 条件后面加上语句:rows between unbounded preceding and unbounded following
可以理解为:当前分组数据中的所有数据进行比较,取最后一条记录
修改后sql:
SELECT
s.sid,
s1.sname,
s1.gender,
c.cname,
s.num,
last_value (num) over (
PARTITION BY c.cname
ORDER BY num DESC
rows BETWEEN unbounded preceding AND unbounded following
) AS last_value用法
FROM
score s
JOIN student s1 ON s.student_id = s1.sid
LEFT JOIN course c ON s.course_id = c.cid;
详细介绍
rows between XXX and XXX
unbounded 无限制的
preceding 分区的当前记录的向前偏移量
current 当前的
following 分区的当前记录的向后偏移量
//比如,需要查询每个月份的累计销售额
CREATE TABLE sale (
id INT PRIMARY KEY auto_increment,
YEAR INT,
MONTH INT,
money FLOAT (10, 2)
);
INSERT INTO sale (YEAR, MONTH, money)
VALUES
(2020, 1, 5840),
(2020, 2, 5780),
(2020, 3, 4300),
(2020, 4, 4760),
(2020, 5, 3630),
(2020, 6, 4130),
(2020, 7, 4350);
select * from sale;
SELECT
MONTH,
sum(money) over(ORDER BY MONTH
rows BETWEEN unbounded preceding AND current ROW //不写默认就是这个
) AS 累计销售额
FROM
sale;