Hive零基础从入门到实战 进阶篇(十八) HiveQL:开窗函数(偏移关联)

目录

 

前言

1. 偏移关联的开窗函数表

2. 函数功能演示

2.1 lag(col,n,default)

2.2 lead(col,n,default)

2.3 first_value(col)

2.4 last_value(col)


 

前言

本文接续上文介绍第四类开窗函数:用于偏移关联的开窗函数。共有4种,分别是:lag、lead、first_value、last_value。

 

1. 偏移关联的开窗函数表

函数名 功能描述
lag(col,n,default) 用于统计分组内往上第n行值。第一个参数为列名,第二个参数为往上第n行(可选,不填默认为1),第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL)。(常用)
lead(col,n,default) 与lag相反,统计分组内往下第n行值。第一个参数为列名,第二个参数为往下第n行(可选,不填默认为1),第三个参数为默认值(当往下第n行为NULL时候,取默认值,如不指定,则为NULL)。(常用)
first_value(col) 用于取分组内排序后,截止到当前行,第一个col的值。(不常用)
last_value(col) 用于取分组内排序后,截止到当前行,最后一个col的值。(不常用)

上述4个函数使用时依旧是后面+开窗函数的固定写法:

over(partition by 列名1,列名2 …… order by 列名3,列名4 …… [desc]

当没有分组时partition by可以省略,但order by不能省略!

 

2. 函数功能演示

数据准备:

新建test.txt文件,输入如下的三列数据,以空格分隔。第一列是月份,第二列代表商铺名称,第三列代表该商铺该月营业额(万元)。

[root@hadoop ~]# vim test.txt 

2019-01 a 1
2019-04 a 4
2019-02 a 2
2019-03 a 3
2019-06 a 6
2019-05 a 5
2019-01 b 2
2019-02 b 4
2019-03 b 6
2019-04 b 8
2019-05 b 10
2019-06 b 12

在hive中新建表temp_test12,将test文件中的数据插入,查看数据。

CREATE TABLE temp_test12 (
      month STRING comment '月份'
      ,shop STRING comment '商铺名称'
      ,money INT comment '营业额(万元)'
      ) row format delimited fields terminated BY ' ';
 
 
load data local inpath '/root/test.txt' into table temp_test12;
 
select * from temp_test12;
 
temp_test12.month	temp_test12.shop	temp_test12.money
2019-01 a 1
2019-04 a 4
2019-02 a 2
2019-03 a 3
2019-06 a 6
2019-05 a 5
2019-01 b 2
2019-02 b 4
2019-03 b 6
2019-04 b 8
2019-05 b 10
2019-06 b 12

2.1 lag(col,n,default)

lag(col,n,default)用于统计分组内往上第n行值。第一个参数为列名,第二个参数为往上第n行(可选,不填默认为1),第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL)。

举例:

新添一列每个商铺上个月的营业额,结果字段如下:  月份    商铺    本月营业额    上月营业额

实现这种格式的中间表后,可以很容易计算出营业额每个月比上个月的涨幅或降幅。

如果没有lag函数,我们想实现这个需求需要这样做:

按照之前所学的内容,实现这个需求我们需要先使用row_number()over按商铺分组,按月份排序得出这样一个结果:
SELECT month
      ,shop
      ,money
      ,ROW_NUMBER() OVER (
            PARTITION BY shop ORDER BY month
            ) AS rn
FROM temp_test12;

结果:
month	shop	money	rn
2019-01	a	1	1
2019-02	a	2	2
2019-03	a	3	3
2019-04	a	4	4
2019-05	a	5	5
2019-06	a	6	6
2019-01	b	2	1
2019-02	b	4	2
2019-03	b	6	3
2019-04	b	8	4
2019-05	b	10	5
2019-06	b	12	6

然后进行偏移自关联,将每个商铺的每个月的营业额和上个月的关联在一起:

WITH a
AS (
      SELECT month
            ,shop
            ,MONEY
            ,ROW_NUMBER() OVER (
                  PARTITION BY shop ORDER BY month
                  ) AS rn
      FROM temp_test12
      )
SELECT a1.month
      ,a1.shop
      ,a1.MONEY
      ,nvl(a2.month, '2018-12') before_month  --为了便于理解,这里加入上月的月份。如果上月没有的月份取为2018-12
      ,nvl(a2.MONEY, 1) before_money          --上月没有的营业额取为1
FROM a a1 --代表本月
LEFT JOIN a a2  --代表上月
      ON a1.shop = a2.shop
            AND a1.month = substr(add_months(CONCAT (
                              a2.month
                              ,'-01'
                              ), 1), 1, 7) --增加月份的函数add_months中至少要传入年月日
GROUP BY a1.month
      ,a1.shop
      ,a1.MONEY
      ,nvl(a2.month, '2018-12')
      ,nvl(a2.MONEY, 1);

结果:
a1.month	a1.shop	a1.money	before_month	before_money
2019-01	a	1	2018-12	1
2019-02	a	2	2019-01	1
2019-03	a	3	2019-02	2
2019-04	a	4	2019-03	3
2019-05	a	5	2019-04	4
2019-06	a	6	2019-05	5
2019-01	b	2	2018-12	1
2019-02	b	4	2019-01	2
2019-03	b	6	2019-02	4
2019-04	b	8	2019-03	6
2019-05	b	10	2019-04	8
2019-06	b	12	2019-05	10  

有了lag函数后实现这个需求变得极为简单:

SELECT month
      ,shop
      ,MONEY
      ,LAG(MONEY, 1, 1) OVER ( --取分组内上一行的营业额,如果没有上一行则取1
            PARTITION BY shop ORDER BY month  --按商铺分组,按月份排序
            ) AS before_money
FROM temp_test12;
   
结果:
month	shop	money	before_money
2019-01	a	1	1
2019-02	a	2	1
2019-03	a	3	2
2019-04	a	4	3
2019-05	a	5	4
2019-06	a	6	5
2019-01	b	2	1
2019-02	b	4	2
2019-03	b	6	4
2019-04	b	8	6
2019-05	b	10	8
2019-06	b	12	10

再调整参数,演示下lag函数的几种其他用法:

SELECT month
      ,shop
      ,MONEY
      ,LAG(MONEY, 1, 1) OVER (
            PARTITION BY shop ORDER BY month
            ) AS before_money
      ,LAG(MONEY, 1) OVER (
            PARTITION BY shop ORDER BY month
            ) AS before_money   --第三个参数不写的话,如果没有上一行值,默认取null
      ,LAG(MONEY) OVER (
            PARTITION BY shop ORDER BY month
            ) AS before_money   --第二个参数不写默认为1,第三个参数不写的话,如果没有上一行值,默认取null,结果与上一列相同
      ,LAG(MONEY, 2, 1) OVER (
            PARTITION BY shop ORDER BY month
            ) AS before_2month_money  --取两个月前的营业额
FROM temp_test12;
   
month	shop	money	before_money	before_money	before_money	before_2month_money
2019-01	a	1	1	NULL	NULL	1
2019-02	a	2	1	1	1	1
2019-03	a	3	2	2	2	1
2019-04	a	4	3	3	3	2
2019-05	a	5	4	4	4	3
2019-06	a	6	5	5	5	4
2019-01	b	2	1	NULL	NULL	1
2019-02	b	4	2	2	2	1
2019-03	b	6	4	4	4	2
2019-04	b	8	6	6	6	4
2019-05	b	10	8	8	8	6
2019-06	b	12	10	10	10	8  

shop为a时,before_money指定了往上第1行的值,如果没有上一行值,默认取null,这里指定为1。
a的第1行,往上1行值为NULL,指定第三个参数取1,不指定取null 
a的第2行,往上1行值为第1行营业额值,1
a的第6行,往上1行值为为第5行营业额值,5

 

2.2 lead(col,n,default)

lead(col,n,default)与lag相反,统计分组内往下第n行值。第一个参数为列名,第二个参数为往下第n行(可选,不填默认为1),第三个参数为默认值(当往下第n行为NULL时候,取默认值,如不指定,则为NULL)。

举例:

新添一列每个商铺下个月的营业额,结果字段如下:  月份    商铺    本月营业额    下月营业额

SELECT month
      ,shop
      ,MONEY
      ,LEAD(MONEY, 1, 7) OVER (
            PARTITION BY shop ORDER BY month
            ) AS after_money
      ,LEAD(MONEY, 1) OVER (
            PARTITION BY shop ORDER BY month
            ) AS after_money   --第三个参数不写的话,如果没有下一行值,默认取null
      ,LEAD(MONEY, 2, 7) OVER (
            PARTITION BY shop ORDER BY month
            ) AS after_2month_money  --取两个月后的营业额
FROM temp_test12;
 
结果:  
month	shop	money	after_money	after_money	after_2month_money
2019-01	a	1	2	2	3
2019-02	a	2	3	3	4
2019-03	a	3	4	4	5
2019-04	a	4	5	5	6
2019-05	a	5	6	6	7
2019-06	a	6	7	NULL	7
2019-01	b	2	4	4	6
2019-02	b	4	6	6	8
2019-03	b	6	8	8	10
2019-04	b	8	10	10	12
2019-05	b	10	12	12	7
2019-06	b	12	7	NULL	7


shop为a时,after_money指定了往下第1行的值,如果没有下一行值,默认取null,这里指定为1。
a的第1行,往下1行值为第2行营业额值,2
a的第2行,往下1行值为第3行营业额值,4
a的第6行,往下1行值为NULL,指定第三个参数取7,不指定取null

 

2.3 first_value(col)

用于取分组内排序后,截止到当前行,第一个col的值。

举例:

SELECT month
      ,shop
      ,MONEY
      ,first_value(MONEY) OVER (
            PARTITION BY shop ORDER BY month
            ) AS first_money
FROM temp_test12;



month	shop	money	first_money
2019-01	a	1	1
2019-02	a	2	1
2019-03	a	3	1
2019-04	a	4	1
2019-05	a	5	1
2019-06	a	6	1
2019-01	b	2	2
2019-02	b	4	2
2019-03	b	6	2
2019-04	b	8	2
2019-05	b	10	2
2019-06	b	12	2

shop为a时,截止到每一行时,分组内的第一行值都是1
shop为b时,截止到每一行时,分组内的第一行值都是2

 

2.4 last_value(col)

用于取分组内排序后,截止到当前行,最后一个col的值。

举例:

SELECT month
      ,shop
      ,MONEY
      ,last_value(MONEY) OVER (
            PARTITION BY shop ORDER BY month
            ) AS last_money
FROM temp_test12;



month	shop	money	last_money
2019-01	a	1	1
2019-02	a	2	2
2019-03	a	3	3
2019-04	a	4	4
2019-05	a	5	5
2019-06	a	6	6
2019-01	b	2	2
2019-02	b	4	4
2019-03	b	6	6
2019-04	b	8	8
2019-05	b	10	10
2019-06	b	12	12

shop为a时,截止到每一行时,分组内的最后一行值都是该行本身
shop为b时,截止到每一行时,分组内的最后一行值都是该行本身

 

 


能看到这里的同学,就右上角点个赞吧,3Q~

 

 

 

你可能感兴趣的:(Hive零基础从入门到实战 进阶篇(十八) HiveQL:开窗函数(偏移关联))