目录
前言
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~
转载:https://blog.csdn.net/qq_23897391/article/details/101195119