1,概念

开窗函数与聚合函数一样,都是对行的集合组进行聚合计算。它用于为行定义一个窗口(这里的窗口是指运算将要操作的行的集合),它对一组值进行操作,不需要使用GROUP BY子句对数据进行分组,能够在同一行中同时返回基础行的列和聚合列。

 2,开窗函数格式

 函数名(列) OVER(partition by … order by …rows|range)

3,具体解释

3.1,分类

1,聚合开窗函数
      即 聚合函数 sum(),count(),max(),min(), avg() + over(partition by … order by …)
2,分组开窗函数
      即row_number(),rank(),dense_rank(),ntile() + over(partition by … order by …) 

3.2,OVER()里参数解释

1,partition by  字段 相当于group by 字段 起到分组作用

2,order by 字段 即根据某个字段进行排序,默认包含该分组的所有行的数据,进行聚合或          排序操作

3,ROWS|RANGE 窗口子句,跟在 order by 子句后面用来限制当前行聚合或排序操作的范        围

4,range和rows的区别:
     rows   是物理窗口,是哪一行就是哪一行,与当前行的值(order by key的key的值)无                    关,只与排序后的行号相关,就是我们常规理解的那样。
     range 是逻辑窗口,与当前行的值有关(order by key的key的值),在key上操作range

               范围   (查看示例代码2,3即可理解)
 

5,窗口子句的几个范围语法的格式:

          current row :当前行
          unbounded proceding  窗口上边界不设限(即区间的第一行)
          unbounded  following   窗口下边界不设限(即区间的最后一行)
          N proceding   当前行之前的N行,可以是数字也可以是能计算数字的表达式
          N  following   当前行之后的N行 ,同上 

3.3,开窗函数的执行顺序

在使用 row_number() over()函数时候,over()里头的分组以及排序的执行晚于 where group by order by 的执行

4,示例解释

4.1,order by 含义解释

eg:sum(a) over (order by b) 的含义如图:按照b列排序,将a依次相加,每次是[窗口第一行,当前行] 数据的累加得到结果,如上: (4+1+6图上写错了)

COUNT DISTINCT 开窗 spark count 开窗函数_字段

4.2,开窗函数示例

4.2.1,表结构

DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `category_id` int(0) NULL DEFAULT NULL,
  `category` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `price` decimal(10, 2) NULL DEFAULT NULL,
  `stock` int(0) NULL DEFAULT NULL,
  `upper_time` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

4.2.2,表的数据

INSERT INTO `goods` VALUES (1, 1, '女装/女士精品', 'T恤', 39.90, 1000, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (2, 1, '女装/女士精品', '连衣裙', 79.90, 2500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (3, 1, '女装/女士精品', '卫衣', 89.90, 1500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (4, 1, '女装/女士精品', '牛仔裤', 89.90, 3500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (5, 1, '女装/女士精品', '百褶裙', 29.90, 500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (6, 1, '女装/女士精品', '呢绒外套', 399.90, 1200, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (7, 2, '户外运动', '自行车', 399.90, 1000, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (8, 2, '户外运动', '山地自行车', 1399.90, 2500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (9, 2, '户外运动', '登山杖', 59.90, 1500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (10, 2, '户外运动', '骑行装备', 399.90, 3500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (11, 2, '户外运动', '运动外套', 799.90, 500, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (12, 2, '户外运动', '滑板', 499.90, 1200, '2020-11-10 00:00:00');
INSERT INTO `goods` VALUES (15, 1, '女装/女士精品1', '连衣裙1', 40.90, 2500, '2020-11-10 00:00:00');

4.2.3,开窗函数查询

1,按商品类别分组,根据价格排序 按rows类型为窗口子句,窗口大小为上无边界,
   下边界为当前行的下一行,这个范围内进行计算个数
select g.*,count(price) over(partition by category_id ORDER BY price 
rows BETWEEN UNBOUNDED PRECEDING AND 1 following)  from goods g

 1,结果如下:

COUNT DISTINCT 开窗 spark count 开窗函数_数据库_02

2,按商品类别分组,根据价格排序,按range类型为窗口子句,窗口大小价格满足[0,当前行的price+1] 
   范围内的所有数据,进行count操作
select g.*,count(price) over(partition by category_id ORDER BY price 
range BETWEEN UNBOUNDED PRECEDING AND 1 following) '无边界到当前行下一行数据',
#窗口大小价格满足[0,当前行的price] 范围内的所有数据 进行count操作 
count(price) over(partition by category_id ORDER BY price 
RANGE BETWEEN UNBOUNDED PRECEDING AND current ROW)  '无边界到当前行数据' from goods g

 2,结果如下,可以参照这个结果进行理解rows和range的区别

COUNT DISTINCT 开窗 spark count 开窗函数_数据库_03

3,lag(col,n,default) 用于统计窗口内往上第n个值,即取每个分区内某列的前面的第n个值。
#    col:列名
#    n:往上第n行
#    default:往上第n行为NULL时候,取默认值,不指定则取NULL
#lag()函数 按rows类型类型为窗口子句 获取上无边界到当前行范围内,当前行往上数第一个值
select g.*,lag(price,1,0) over(partition by category_id ORDER BY price 
rows BETWEEN UNBOUNDED PRECEDING AND current ROW)  from goods g

3,结果如下,可以用于获取当前数据行的 上次登录时间 的需求

COUNT DISTINCT 开窗 spark count 开窗函数_窗口大小_04

4,lead(col,n,default) 用于统计窗口内往下第n个值,即每个分区内某列的后n个值。
#    col:列名
#    n:往下第n行
#    default:往下第n行为NULL时候,取默认值,不指定则取NULL
#lag()函数 按rows类型类型为窗口子句 获取上无边界到当前行范围内,当前行往下数第一个值
select g.*,lead(price,1,0) over(partition by category_id ORDER BY price
 rows BETWEEN UNBOUNDED PRECEDING AND current ROW)  from goods g

4,结果如下,结合lead()函数 可以获取用户 上次登录时间与下次登录时间的 需求

COUNT DISTINCT 开窗 spark count 开窗函数_字段_05

5,first_value 开窗函数 按rows类型类型为窗口子句,获取指定窗口内的第一个值
   last_value 开窗函数 按rows类型类型为窗口子句,获取指定窗口内的最后一个值

#下述例子 获取每次窗口大小为 第一行(无边界)到当前行, 以id分组的第一个值
select g.*,first_value(price) 
over(partition by category_id ORDER BY price rows BETWEEN UNBOUNDED PRECEDING AND current ROW) '分区中第一个值',
last_value(price)
over(partition by category_id ORDER BY price rows BETWEEN UNBOUNDED PRECEDING AND current ROW) '分区中最后一个值'
from goods g

5,结果如下,可以用于指定时间内最新或最旧数据的需求。

COUNT DISTINCT 开窗 spark count 开窗函数_数据_06

6,cume_dist 返回小于等于当前值的行数/分组内总行数。
# 计算某个窗口或分区中某个值的累积分布。假定升序排序,则使用以下公式确定累积分布:
# 小于等于当前值x的行数 / 窗口或partition分区内的总行数。
# 其中,x 等于 order by 子句中指定的列的当前行中的值。
# 1、g中指定partition,所以是以指定的字段进行分组进行统计,比如id为1的price=29.9,
#    则小于等于29.9的只有1行数据,整个分组为6行,即1/6 = 0.166,其余返回结果同理可得。
select g.*,cume_dist() over(partition by category_id ORDER BY price)  from goods g

6,结果如下,可用于求比例的需求

COUNT DISTINCT 开窗 spark count 开窗函数_数据_07

7,row_number开窗函数 
#从1开始对分区内的数据排序
select g.*,row_number() over(partition by category_id ORDER BY price)  from goods g

 7,结果如下:

COUNT DISTINCT 开窗 spark count 开窗函数_窗口大小_08

7, rank开窗函数
# rank 开窗函数基于 over 子句中的 order by 确定一组值中一个值的排名。如果存在partition by ,
# 则为每个分区组中的每个值排名。排名可能不是连续的。例如,如果两个行的排名为 1,则下一个排名为 3。
select g.*,rank() over(partition by category_id ORDER BY price)  from goods g

7,结果如下:

COUNT DISTINCT 开窗 spark count 开窗函数_窗口大小_09

8,dense_rank开窗函数
#dense_rank与rank有一点不同,当排名一样的时候,接下来的行是连续的。如两个行的排名为 1,则下一个排名为 2。
select g.*,dense_rank() over(partition by category_id ORDER BY price)  from goods g

8,结果如下

COUNT DISTINCT 开窗 spark count 开窗函数_数据库_10

9,percent_rank开窗函数
#计算给定行的百分比排名。可以用来计算超过了百分之多少的人。
#即:(当前行的rank值-1)/(分组内的总行数-1)
select g.*,percent_rank() over(partition by category_id ORDER BY price)  from goods g

 9,结果如下:

COUNT DISTINCT 开窗 spark count 开窗函数_数据库_11

10, ntile开窗函数
# 函数功能:NTILE(n),将每个分区内排序后的结果均分成N份。本质是将每个分区拆分成更小的分区。
# 如果切片不均匀,默认增加第一个切片的分布。
# NTILE不支持ROWS BETWEEN。
select g.*,ntile(4) over(partition by category_id ORDER BY price)  from goods g

,10,结果如下

COUNT DISTINCT 开窗 spark count 开窗函数_数据_12