FIRST 阶段一
一、MYSQL中查询五子句
select * from 数据表 ① where子句
② group by 分组子句
③ having子句
④ order by子句
⑤ limit子句
注意事项:①②③④⑤五子句是SQL中最重要的五个子句,而且其顺序不能颠倒
二、查询语句
select * from tb_student;
select id,name,address from tb_student;
1、回顾where子句
(1)等于 = ; 不等于 !=,<>
# 查询tb_student表中姓名为‘貂蝉’的同学的所有信息
select * from tb_student where name = '貂蝉';
# 查询tb_student表中姓名不为‘貂蝉’的同学的所有信息
select * from tb_student where name != '貂蝉';
select * from tb_student where name <> '貂蝉';
# 查询tb_student表中年龄大于20岁的同学信息
select * from tb_student where age > 20;
(2)查询结合与and、或or、非not
# 查询年龄为34岁且性别为male的同学信息
select * from tb_student where age = 34 and gender = 'male';
# 查询id编号为1或3或5的同学信息
select * from tb_student where id = 1 or id = 3 or id = 5;
# 查询id编号不为1或3或5的同学信息
select * from tb_student where id not in(1,3,5);
(3)where子句与like模糊查询,like模糊查询有两个符合 %百分号 和 _下划线
# %代表任意个任意字符
# _代表任意的某个字符(只能表示一个字符)
# 查询班级中姓“孙”的同学信息
select * from tb_student where name like '孙%';
# 查询班级中姓'刘'且姓名为两位数的同学的信息
select * from tb_student where name like '孙_';
# 查询班级中以'婵'字结尾的同学信息
select * from tb_student where name like '%婵';
# 查询班级中包含'乔'字的同学信息
select * from tb_student where name like '%乔%';
(4)空值判断(NULL)
# 查询age年龄为NULL的同学信息
select * from tb_student where age is null;
# 查询age年龄不为NULL的同学信息
select * from tb_student where age is not null;
2、order by 子句,(排序子句,升序12345、降序54321)
# order by 不仅可以对数字进行排序,还可以对字母进行排序
# (升序12345,降序54321)
# order by 字段名称 asc
# order by 字段名称 desc
# 查询班级中所有同学信息,按年龄从小到大排序
select * from tb_student order by age asc;
# 注-在排序时,null一般被认为是最小值
# 多个字段进行排序,先排order最左侧的字段,当字段值相同时,则继续按右侧的字段进行排序(了解)
select * from tb_student order by age asc,height asc;
3、limit子句(五子句的最后一个)
# 只要最高的
select * from tb_student order by age desc limit 1;
# 要第二第三
select * from tb_student order by age desc limit 1,2;
三、常见的聚合函数
1. count(col): 表示求指定列的总行数
2. max(col): 表示求指定列的最大值
3. min(col): 表示求指定列的最小值
4. sum(col): 表示求指定列的和
5. avg(col): 表示求指定列的平均值
# 求班级中所有同学的数量
select count(id) from tb_student;
# 求班级中年龄最大值
select max(age) from tb_student;
# 求班级中年龄最小值
select min(age) from tb_student;
# 求班级中所有年龄总和
select sum(age) from tb_student;
# 求班级中年龄平均值
select avg(age) from tb_student;
# 扩展 对null处理的函数ifnull(列名,默认值),如果某个列的值为null,则系统绘自动将其设置为右边的默认值
# 求班级中所有同学的平均值
select avg(age) from tb_student;
select avg(ifnull(age,0) from tb_student;
四、分组函数
# group by 分组子句:顾名思义,要把分析的数据通过某种形式进行分组,如:按学科、性别、车次、部门等
# 为什么要进行分组?为了更好地进行数据地统计操作(group by结合5个聚合函数 )
select * from tb_student group by gender;------------错误
# > 1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db_itheima.tb_student.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
select x from tb_student group by x;
# 统计班级中男女同学的比例(男同学多少人,女同学多少人)
select count(id) from tb_student where gender = 'male';
select count(id) from tb_student where gender = 'female';
select gender, count(*) from tb_student group by gender;
# select从数据表中读取数据就是一个循环遍历的过程,首先读取第一组数据,然后读取第二条数据,以此类推--直至数据遍历结束
# 求每个学科中,年龄最大的
select subject,max(age) from tb_student group by subject;
# having子句,对查询的结果进行过滤和筛选
# 普通查询中,having子句可以替代where子句(含义相同)
select * from tb_student where age > 20;
select * from tb_student having age > 20;
# 在有group by分组子句中,having和where有很大的不同
# 对分组后的结果进行过滤和筛选
# 根据gender字段进行分组,统计分组条数大于2条以上的分组信息
select gender,count(id) from tb_student group by gender having count(id) > 2;
SECONDE阶段二
一、MySQL8.0窗口函数概述
# ØMYSQL 8.0 之后,加入了窗口函数功能,简化了数据分析工作中查询语句的书写
# 在没有窗口函数之前,我们需要通过定义临时变量和大量的子查询才能完成的工作,使用窗口函数会更加简洁高效
# 窗口函数是数据分析工作中必须掌握的工具,在SQL笔试中也是高频考点
1、什么是窗口函数
窗口函数是类似于可以返回聚合值的函数,例如SUM(),COUNT(),MAX()。但是窗口函数又与普通的聚合函数不同,它不会对结果进行分组,使得输出中的行数与输入中的行数相同。
2、窗口函数长这样
SELECT SUM() OVER(PARTITION BY ___ ORDER BY___) FROM Table
- 聚合功能:在上述例子中,我们用了SUM(),但是你也可以用COUNT(), AVG()之类的计算功能
- PARTITION BY:你只需将它看成GROUP BY子句,但是在窗口函数中,你要写PARTITION BY
- ORDER BY:ORDER BY和普通查询语句中的ORDER BY没什么不同。注意,输出的顺序要仔细考虑
3、窗口函数的优点(小结)
简单
窗口函数更易于使用。在上面的示例中,与使用聚合函数然后合并结果相比,使用窗口函数仅需要多一行就可以获得所需要的结果。
快速
这一点与上一点相关,使用窗口函数比使用替代方法要快得多。当你处理成百上千个千兆字节的数据时,这非常有用。
多功能性
最重要的是,窗口函数具有多种功能,比如,添加移动平均线,添加行号和滞后数据,等等。
二、窗口函数基本语法
窗口函数基本语法
<window_function> OVER (...)
/*
<window_function>: 这里可以是我们之前已经学过的聚合函数,比如(`COUNT()`, `SUM()`, `AVG()` 等)。也可以是其他函数,比如ranking 排序函数,分析函数等,后面的课程中会介绍。
OVER(...):窗口函数的窗框通过`OVER(...)` 子句定义,窗口函数中很重要的部分就是通过`OVER(...)` 定义窗框 (开窗方式和大小)
*/
三、窗口函数中OVER()子句详解
over()基本语法
Ø首先看`OVER (...)` 的最基本用法: `OVER()` 意思是所有的数据都在窗口中,看下面的SQL
SELECT
first_name,
last_name,
salary,
AVG(salary) OVER()
FROM employee;
# SQL并不复杂,主要看跟 `OVER()` 相关的部分
AVG(salary) OVER()
# `AVG(salary)` 意思是要计算平均工资,加上 `OVER()` 意味着对全部数据进行计算,所以就是在计算所有人的平均工资。
案例演示
# 创建报表,除了查询每个人的工资之外,还要统计出公司每月的工资支出
SELECT
first_name,
last_name,
salary,
SUM(salary) OVER()
FROM employee;
# 查询结果:
# 统计采购表中的平均采购价格,并与明细一起显示(每件物品名称,价格)
SELECT
item,
price,
AVG(price) OVER()
FROM purchase;
# 通常,`OVER()`用于将当前行与一个聚合值进行比较,例如,我们可以计算出员工的薪水和平均薪水之间的差。
SELECT
first_name,
last_name,
salary,
AVG(salary) OVER(),
salary - AVG(salary) OVER() as difference
FROM employee;
# 上面查询结果的最后一列显示了每名员工的工资和平均工资之间的差额,这就是"窗口函数的典型应用场景:将当前行与一组数据的聚合值进行比较"。
/*
需求:创建报表统计每个员工的工龄和平均工龄之间的差值。
报表中包含如下字段:
员工的名字,员工的姓氏,员工的工龄,所有员工的平均工龄,员工工龄和平均工龄之间的差值
*/
SELECT
first_name,
last_name,
years_worked,
AVG(years_worked) OVER(),
years_worked - AVG(years_worked) OVER() as difference
FROM employee;
# 接下来我们看一下, `OVER()` 与 `COUNT()` 如何组合使用:
SELECT
id,
name,
COUNT(id) OVER()
FROM department
ORDER BY name ASC;
# 在上面的SQL中,我们查询出每个部门的 `id` 和 `name` 部门名称,以及所有部门的数量,最后通过部门名字排序。
# 需要注意:窗口函数在`WHERE` 子句后执行!
SELECT
first_name,
last_name,
salary,
AVG(salary) OVER(),
salary - AVG(salary) OVER()
FROM employee
WHERE department_id = 1;
SELECT
first_name,
last_name,
salary,
AVG(salary) OVER() as avg
FROM employee
WHERE department_id IN (1, 2, 3);
四、OVER( PARTITION BY )的使用
# 接下来,我们来介绍如何在`OVER()`中添加 `PARTITION BY`,基本的语法如下:
<window_function> OVER (PARTITION BY column1, column2 ... column_n)
# `PARTITION BY` 的作用与 `GROUP BY`类似:将数据按照传入的列进行分组,与 `GROUP BY` 的区别是, `PARTITION BY` 不会改变结果的行数。
# PARTITION BY与GROUP BY区别
/*
① group by是分组函数,partition by是分析函数
② 在执行顺序上:from > where > group by > having > order by,而partition by应用在以上关键字之后,可以简单理解为就是在执行完select之后,在所得结果集之上进行partition by分组
③ partition by相比较于group by,能够在保留全部数据的基础上,只对其中某些字段做分组排序(类似excel中的操作),而group by则只保留参与分组的字段和聚合函数的结果(类似excel中的pivot透视表)
*/
# 需求:查询每种类型火车的ID,型号,一等座数量,同型号一等座数量总量
SELECT
id,
model,
first_class_places,
SUM(first_class_places) OVER (PARTITION BY model)
FROM train;
# 如果用`GROUP BY`实现上面相同的需求,我们需要通过子查询将结果与原始表`JOIN` , 对比起来,使用窗口函数的实现更加简洁。
SELECT
id,
train.model,
first_class_places,
c.sum
FROM
train
JOIN ( SELECT model, SUM( first_class_places ) AS sum FROM train GROUP BY model ) AS c
ON train.model = c.model
SELECT
id,
date,
COUNT(id) OVER(PARTITION BY date) as count
FROM journey;
SELECT
id,
model,
first_class_places,
second_class_places,
COUNT(id) OVER (PARTITION BY model)
FROM train
WHERE first_class_places > 30
AND second_class_places > 180;
SELECT
journey.id,
journey.date,
train.model,
train.max_speed,
MAX(max_speed) OVER(PARTITION BY route_id, date)
FROM journey
JOIN train
ON journey.train_id = train.id;
SELECT
journey.id,
production_year,
COUNT(journey.id) OVER(PARTITION BY train_id) as count_train,
COUNT(journey.id) OVER(PARTITION BY route_id) as count_journey
FROM train
JOIN journey
ON train.id = journey.train_id;
/*
OVER(PARTITION BY x)的工作方式与GROUP BY类似,将x列中,所有值相同的行分到一组中
PARTITON BY 后面可以传入一列数据,也可以是多列(需要用逗号隔开列名)
*/
五、排序函数
-
最基本的排序函数:
RANK() OVER(ORDER BY column1, column2...)
-
通过排序获取序号的函数介绍了如下三个:
-
RANK() – 返回排序后的序号 rank ,有并列的情况出现时序号不连续
-
DENSE_RANK() – 返回 连续 序号
-
ROW_NUMBER() – 返回连续唯一的行号,与排序
ORDER BY
配合返回的是连续不重复的序号
-
-
NTILE(x) – 将数据分组,并为每组添加一个相同的序号
-
WITH 获取排序后,指定位置的数据(第一位,第二位)可以通过如下
WITH ranking AS (SELECT RANK() OVER (ORDER BY col2) AS RANK, col1 FROM table_name) SELECT col1 FROM ranking WHERE RANK = place1;
`RANK()`会返回每一行的等级(序号)
`ORDER BY`对行进行排序将数据按升序或降序排列
` RANK()OVER(ORDER BY ...)`是一个函数,与`ORDER BY` 配合返回序号
dens_rank
/*
RANK() 函数返回的序号,可能会出现不连续的情况
如果想在有并列情况发生的时候仍然返回连续序号可以使用dense_rank()函数
将前面的例子做一个修改
*/
SELECT
name,
platform,
editor_rating,
DENSE_RANK() OVER(ORDER BY editor_rating) as rank_
FROM game;
ROW_NUMBER()
SELECT
name,
platform,
editor_rating,
ROW_NUMBER() OVER(ORDER BY editor_rating) `row_number`
FROM game;
`NTILE(X)`函数将数据分成X组,并给每组分配一个数字(1,2,3....),例如:
SELECT
name,
genre,
editor_rating,
NTILE(3) OVER (ORDER BY editor_rating DESC)
FROM game;
/*
在上面的查询中,通过 `NTILE(3)` 我们根据`editor_rating` 的高低,将数据分成了三组,并且给每组指定了一个标记
1 这一组是评分最高的
3 这一组是评分较低的
2 这一组属于平均水平
*/
六、window frames 自定义窗口
window frames基本语法
# 定义 window frames 有两种方式: `ROWS` 和 `RANGE` ,具体语法如下:
<window function> OVER (...
ORDER BY <order_column>
[ROWS|RANGE] <window frame extent>
)
# 上面的SQL框架中,... 代表了之前我们介绍的例如 `PARTITION BY` 子句,下面我们先关注 `ROWS` 和 `RANGE`的用法,然后再加上`PARTITION BY`
SELECT
id,
total_price,
SUM(total_price) OVER(
ORDER BY placed
ROWS UNBOUNDED PRECEDING) as `sum`
FROM single_order
/*
在上面的查询中,我们对`total_price`列求和。 对于每一行,我们将当前行与它之前的所有行( "UNBOUNDED PRECEDING" )相加,`total_price`列相当于到当前行的累加和,这个值随着当前行的变化而增加
*/
ROWS BETWEEN lower_bound AND upper_bound
/*
上面小结中介绍过,我们有两种方式来定义窗口大小(window frames), `ROWS`和`RANGE`
我们先介绍比较容易理解的`ROWS`方式,通用的语法如下:
在上面的框架中,BETWEEN ... AND ... 意思是在... 之间,上限(upper_bund)和下限(lower_bound)的取值为如下5种情况:
UNBOUNDED PRECEDING – 对上限无限制
n PRECEDING – 当前行之前的第 n 行 ( n ,填入具体数字如:5 PRECEDING )
CURRENT ROW – 仅当前行
n FOLLOWING –当前行之后的第 n 行 ( n ,填入具体数字如:5 FOLLOWING )
UNBOUNDED FOLLOWING – 对下限无限制
*/
# 需求:统计到当前行为止的累计下单金额(running_total),以及前后3天下单金额总和(sum_3_before_after)。
SELECT
id,
total_price,
SUM(total_price) OVER(ORDER BY placed ROWS UNBOUNDED PRECEDING) AS running_total,
SUM(total_price) OVER(ORDER BY placed ROWS between 3 PRECEDING and 3 FOLLOWING) AS sum3_before_after
FROM single_order; _
# 需求:按下单日期排序,统计订单日期,下单日期,到当前行为止的累计下单数量
SELECT
id,
placed,
COUNT(id) OVER(
ORDER BY placed
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS `count`
FROM single_order;
# 需求:仓库发货时需要手工拣货。 对于order_id = 5的订单,计算未分拣的商品数量总和。 对于该订单中的每种商品,按升序查询起出货明细中的ID,产品ID,产品数量和剩余未拣货商品的数量(包括当前行)
SELECT
id,
product_id,
quantity,
SUM(quantity) OVER(
ORDER BY id
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS `sum`
FROM order_position
WHERE order_id = 5;
# 需求:统计每件商品的上架日期,以及截至值该日期,上架商品种类数量
SELECT
id,
name,
introduced,
COUNT(id) OVER(
ORDER BY introduced
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
FROM product;
ROWS和RANGE区别演示
/*
和使用 `ROWS`一样,使用`RANGE` 一样可以通过 `BETWEEN ... AND...` 来自定义窗口
在使用`RANGE` 时,我们一般用
`RANGE UNBOUNDED PRECEDING`
`RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING`
`RANGE CURRENT ROW`
但是在使用`RANGE` 确定窗口大小是,一般 不与 n PRECEDING 或 n FOLLOWING 一起使用
使用ROWS,通过当前行计算前n行/后n行,很容易确定窗口大小
使用RANGE,是通过行值来进行判断,如果使用 3 PRECEDING 或 3 FOLLOWING 需要对当前行的值进行-3 或者+3操作,具体能选中几行很难确定,通过WINDOW FRAMES 我们希望定义的窗口大小是固定的、可预期的,但当RANGE 和 n PRECEDING 或 n FOLLOWING 具体会选中几行数据,跟随每行取值不同而发生变化,窗口大小很可能不固定。
*/
/*
我们可以在OVER(...)中定义一个窗口框架。 语法为:
[ROWS | RANGE] <window frames 定义>
ROWS 按行来处理数据(例如ROW_NUMBER()函数)
RANGE按行值来处理数据(例如RANK()函数)
`<window frame definition>` 按如下方式定义: `BETWEEN <lower bound> AND <upper bound>`, 其中边界通过以下方式定义:
UNBOUNDED PRECEDING`
n PRECEDING (ROWS only)
CURRENT ROW
n FOLLOWING (ROWS only)
UNBOUNDED FOLLOWING
*/
七、分析函数
LEAD(X)函数
与聚类函数不同的地方是,分析函数只引用窗口中的单个行
SELECT
name,
opened,
LEAD(name) OVER(ORDER BY opened)
FROM website;
# 上面的SQL中,分析函数为LEAD(name)。 LEAD中传入name列作为参数,将以 `ORDER BY` 排序后的顺序,返回当前行的下一行`name` 列所对应的值,并在新列中显示,具体如下图所示:
# 需求: 统计id 为1的网站,每天访问的人数以及下一天访问的人数- 返回字段:`day`日期,`users`访问人数,`lead` 下一天访问人数
SELECT
day,
users,
LEAD(users) OVER(ORDER BY day) AS `lead`
FROM statistics
WHERE website_id = 1;
# lead函数在计算增量的时候非常有用,比如我们想比较同一列两个值的差值
SELECT
day,
clicks,
LEAD(clicks) OVER(ORDER BY day),
LEAD(clicks) OVER(ORDER BY day) - clicks
FROM statistics
WHERE website_id = 2;
/*
上面的查询计算了每日增量:最后一列显示了当日与次日之间的点击次数差异
从业务角度来看,这可以很容易地告诉我们有关该网站的很多信息
如果大多数增量是正的,且增量在逐渐变大,那么该网站业务可能处于上升期
如果大多数是负的,那么需要找到收入下滑的原因
*/
# 需求:统计id为1的网站,每日收入,后一天收入,以及每日收入的环比。
SELECT
day,
revenue,
LEAD(revenue) OVER(ORDER BY day) as `lead`,
LEAD(revenue) OVER(ORDER BY day) - revenue as diff
FROM statistics
WHERE website_id = 1;
# LEAD函数传入两个参数LEAD(x,y)
LEAD函数还可以传入两个参数:
参数1 跟传入一个参数时的情况一样:一列的列名
参数2 代表了偏移量,如果传入2 就说明要以当前行为基准,向前移动两行作为返回值
SELECT
name,
opened,
LEAD(opened,2) OVER(ORDER BY opened)
FROM website;
# 上面的SQL中,LEAD函数传入了2,当前行为第一行时,会返回第三行的值作为LEAD函数的结果
/*
需求:统计id为2的网站,在2016年5月1日到5月14日之间,每天的用户访问数量以及7天后的用户访问数量
需要注意,最后7行最后一列会返回NULL,因为最后7行没有7日后的数据。
*/
SELECT
day,
users,
LEAD(users, 7) OVER(ORDER BY day) AS `lead`
FROM statistics
WHERE website_id = 2
AND day BETWEEN '2016-05-01' AND '2016-05-14';
# LEAD函数传入两个参数LEAD(x,y,z)
lead函数也可以接收三个参数,第三个参数用来传入默认值,应用场景是当使用lead函数返回null的时候,可以用第三个参数传入的默认值进行填充
练习44中,后7行出现了null,这里可以传入默认值,如-1,用来避免出现null的情况
SELECT
day,
users,
LEAD(users, 7, -1) OVER(ORDER BY day) AS `lead`
FROM statistics
WHERE website_id = 2
AND day BETWEEN '2016-05-01' AND '2016-05-14';
LAG(X)函数
# LAG(x)函数与LEAD(x)用法类似,区别是,LEAD返回当前行后面的值,LAG返回当前行之前的值
SELECT
name,
opened,
LAG(name) OVER(ORDER BY opened)
FROM website;
/*
`LEAD(...)` 和 `LAG(...)`,之间可以互相替换,可以在ORDER BY的时候通过`DESC` 来改变排序方式,使`LEAD(...)` 和 `LAG(...)`返回相同结果,比如 :
*/
LEAD (...) OVER(ORDER BY ...)
=
LAG (...) OVER (ORDER BY ... DESC)
LEAD (...) OVER(ORDER BY ... DESC)
=
LAG (...) OVER (ORDER BY ...)
/*
LEAD 和 LAG 容易记混
LEAD 领先的意思 找行号更大的数据
LAG 落后的意思 找行号更小的数据
*/
FIRST_VALUE(x)与LAST_VALUE(x)
# FISRT_VALUE函数,从名字中能看出,返回指定列的第一个值
SELECT
name,
opened,
budget,
FIRST_VALUE(budget) OVER(ORDER BY opened)
FROM website;
# 需求:统计id为2的网站每天用户访问情况,以及最少用户访问人数。
SELECT
day,
users,
FIRST_VALUE(users) OVER(ORDER BY users) as `first_value`
FROM statistics
WHERE website_id = 2;
# FIRST_VALUE(x)返回第一个值,LAST_VALUE(x)返回最后一个值
# LAST_VALUE 与 window frame
在上面的例子中,我们没有得到想要的结果,回顾一下之前我们所介绍的 window frame
当OVER子句中包含ORDER BY时,如果我们不显式定义window frame,SQL会自动带上默认的window frame语句:
RANGE UNBOUNDED PRECEDING, 意味着我们的查询范围被限定在第一行到当前行(current row)
如果想通过LAST_VALUE 与ORDER BY配合得到所有数据排序后的最后一个值,需要吧window frame语句RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
或者
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
SELECT
name,
opened,
LAST_VALUE(opened) OVER(
ORDER BY opened
RANGE BETWEEN UNBOUNDED PRECEDINGAND AND UNBOUNDED FOLLOWING
) AS `last_value`
FROM website;
NTH_VALUE(x,n) 函数
# 需求:NTH_VALUE(x,n) 函数返回 x列,按指定顺序的第n个值
SELECT
name,
opened,
NTH_VALUE(opened, 2) OVER(
ORDER BY opened
ROWS BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING)
FROM website;
/*
上面的SQL将数据按照开业日期排序,NTH_VALUE(opened,2) 返回开业日期排在第二位的值
需要注意,我们需要调整window frame 否则某些情况下不能返回正确的数据
提示:可以在排序的时候加上DESC 调整排序的顺序,配合 NTH_VALUE(x,n) 在某些场景下更加方便
*/
分析函数小结
/*
LEAD(x) 和 LAG(x) 分别返回传入的列x对于当前行的下一行/前一行的值
LEAD(x,y) 和 LAG(x,y) 分别返回传入的列x对于当前行的后y行/前y行的值
FIRST_VALUE(x) 和 LAST_VALUE(x) 分别返回列x 的第一个值/最后一个值
NTH_VALUE(x,n) 返回 x 列的 第n个值
LAST_VALUE 和 NTH_VALUE 通常要求把window frame修改成 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
*/
八、PARTITION BY 与 ORDER BY
RANK() 与 PARTITION BY ORDER BY
SELECT
id,
country,
city,
rating,
RANK() OVER(PARTITION BY country ORDER BY rating DESC) AS `rank`
FROM store;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9WJg0lSi-1646847417123)(C:\Users\86187\AppData\Roaming\Typora\typora-user-images\image-20220310012036127.png)]
# 我们也可以使用其它排序函数如NTILE(x), 用法与前面的RANK完全一致
SELECT
id,
country,
city,
rating,
NTILE(2) OVER(PARTITION BY country ORDER BY opening_day)
FROM store;
# 我们可以在 CTE 中使用PARTITION BY ORDER BY 将数据进一步分组,对每组进一步排序。
WITH ranking AS (
SELECT
country,
city,
RANK() OVER(PARTITION BY country ORDER BY rating DESC) AS `rank`
FROM store
)
SELECT
country,
city
FROM ranking
WHERE `rank` = 1;
/*
在上面的SQL中,我们查询除了每个国家顾客评价最高的商店所在城市
通过在CTE中使用窗口函数来获取每个国家/地区按评分的商店排名
在外部查询中直接查询每个国家分数最高的店铺所在城市
*/
九、窗口函数避坑指南
不能在WHERE子句中使用窗口函数
我们在第一小节介绍过,Where 条件中不能使用窗口函数,原因是SQL的执行顺序决定了窗口函数实在Where子句之后执行的,具体执行顺序如下:
FROM
WHERE
GROUP BY
聚合函数
HAVING
窗口函数
SELECT
DISTINCT
UNION
ORDER BY
OFFSET
LIMIT
不能在HAVING子句中使用窗口函数
不能在GROUP BY子句中使用窗口函数
在ORDER BY中使用窗口函数
通过上面的例子我们知道,只能在SELECT 和 ORDER BY子句中使用窗口函数,WHERE HAVING GROUP BY中只能使用子查询。
接下来我们来看一下是否能在ORDER BY子句中使用窗口函数
需求:将所有的拍卖按照浏览量降序排列,并均分成4组,最终结果再按照每组编号升序排列,返回字段: `id`, `views` 和 分组情况( `quartile`)
SELECT
id,
views,
NTILE(4) OVER(ORDER BY views DESC) AS quartile
FROM auction
ORDER BY NTILE(4) OVER(ORDER BY views DESC);
窗口函数与GROUP BY一起使用
Rank****时使用聚合函数
我们可以在聚合函数的结果上使用RANK函数,看下面的例子:
SELECT
country,
COUNT(id),
RANK() OVER(ORDER BY COUNT(id) DESC) AS `rank`
FROM auction
GROUP BY country;
利用GROUP BY计算环比
我们可以利用GROUP BY 分组之后,结合LEAD, LAG 计算环比(相邻两天的差值),看下面的SQL:
SELECT
ended,
SUM(final_price) AS sum_price,
LAG(SUM(final_price)) OVER(ORDER BY ended) AS `lag`
FROM auction
GROUP BY ended
ORDER BY ended;
对GROUP BY分组后的数据使用 PARTITION BY
MySQL窗口函数小结
小结
- Ø窗口函数只能出现在SELECT和ORDER BY子句中
- Ø如果查询的其他部分(WHERE,GROUP BY,HAVING)需要窗口函数,请使用子查询,然后在子查询中在使用窗口函数
- Ø如果查询使用聚合或GROUP BY,请记住窗口函数只能处理分组后的结果,而不是原始的表数据
中使用窗口函数
需求:将所有的拍卖按照浏览量降序排列,并均分成4组,最终结果再按照每组编号升序排列,返回字段:id
,views
和 分组情况(quartile
)
SELECT
id,
views,
NTILE(4) OVER(ORDER BY views DESC) AS quartile
FROM auction
ORDER BY NTILE(4) OVER(ORDER BY views DESC);
### 窗口函数与GROUP BY一起使用
### **Rank****时使用聚合函数**
```sql
我们可以在聚合函数的结果上使用RANK函数,看下面的例子:
SELECT
country,
COUNT(id),
RANK() OVER(ORDER BY COUNT(id) DESC) AS `rank`
FROM auction
GROUP BY country;
利用GROUP BY计算环比
我们可以利用GROUP BY 分组之后,结合LEAD, LAG 计算环比(相邻两天的差值),看下面的SQL:
SELECT
ended,
SUM(final_price) AS sum_price,
LAG(SUM(final_price)) OVER(ORDER BY ended) AS `lag`
FROM auction
GROUP BY ended
ORDER BY ended;
对GROUP BY分组后的数据使用 PARTITION BY
MySQL窗口函数小结
小结
- Ø窗口函数只能出现在SELECT和ORDER BY子句中
- Ø如果查询的其他部分(WHERE,GROUP BY,HAVING)需要窗口函数,请使用子查询,然后在子查询中在使用窗口函数
- Ø如果查询使用聚合或GROUP BY,请记住窗口函数只能处理分组后的结果,而不是原始的表数据