Hive常用函数总结(53个)

目录

1.常用日期函数(22个)

1.1unix_timestamp

 1.2from_unixtime

1.3from_utc_timestamp

1.4current_date

1.5current_timestamp

1.6to_date

1.7year

1.8month

1.9day

1.10hour

1.11minute

1.12second

1.13weekofyear

1.14dayofmonth

1.15dayofweek

1.16months_betwee

1.17add_months

1.18datediff

1.19date_add

1.20date_sub

1.21last_day

1.22date_format

2.常用取整函数(3个)

2.1round

2.2ceil

2.3floor

3.常用字符串操作函数(8个)

3.1upper

3.2lower

3.3length

3.4trim

3.5lpad

3.6rpad

3.7regexp_replace

3.8substring

4.集合操作

4.1size

4.2map_keys

4.3map_values

4.4array_contains

4.5sort_array

5.其他常用内置函数

5.1nvl

5.2case when then [when then] else end [别名]

5.3行转列

5.3.1concat

5.3.2concat_ws

5.3.3collect_set

5.3.4collect_list

5.4列传行

5.4.1explode

5.4.2split

5.4.3laterval view

5.5开窗函数

5.5.1定义及用法

5.5.2range和rows

5.5.3实例

5.6排名函数

5.6.1rank

5.6.2dens_rank

5.6.3row_number

5.7first_value

5.8last_value

6.自定义函数

6.1函数分类

6.2实现步骤

6.3引入自定义函数步骤

6.4自定义UDF函数

6.4.1需求

 6.4.2实现

6.4.3创建临时函数 

6.4.4创建永久函数


1.常用日期函数(22个)

1.1unix_timestamp

        返回当前或指定时间的时间戳(秒)

select unix_timestamp();	-- 1657173785
select unix_timestamp("2020-06-14","yyyy-MM-dd");   -- 1592092800
select unix_timestamp("2020/06/14","yyyy/MM/dd");   -- 1592092800
select unix_timestamp("2020/06/14 15:23:59","yyyy/MM/dd HH:mm:ss");   -- 1592148239

 1.2from_unixtime

                将时间戳(秒)转为日期+时间格式

select from_unixtime(1592092800);    -- 2020-06-14 00:00:00
select from_unixtime(1592148239);    -- 2020-06-14 15:23:59

1.3from_utc_timestamp

        from_utc_timestamp

select from_utc_timestamp(1592148239000,'Asia/HongKong');   -- 2020-06-14 15:23:59.000000000
select from_utc_timestamp(1592148239000,'Asia/Tokyo');   -- 2020-06-15 00:23:59.000000000

1.4current_date

        返回当前日期

select `current_date`(); -- 2022-07-07

1.5current_timestamp

        当前的日期加时间

select `current_timestamp`();   -- 2022-07-07 14:22:41.118000000

1.6to_date

        抽取日期部分

select to_date('2020-06-14 15:23:59');  -- 2020-06-14

1.7year

        获取年

select year('2020-06-14 15:23:59'); -- 2020
select year('2021-06-14');  -- 2021

1.8month

        获取月

select month('2020-06-14 15:23:59'); -- 6
select month('2021-03-14');  -- 3

1.9day

        获取日

select day('2020-06-19 15:23:59'); -- 19
select day('2021-03-24');  -- 24

1.10hour

        获取时

select hour('2020-06-19 15:23:59'); -- 15
select hour('15:23:59'); -- null

1.11minute

        获取分

select minute('2020-06-19 15:23:59');   -- 23
select minute('15:23:59');   -- null

1.12second

        获取秒

select second('2020-06-19 15:23:59');   -- 59
select second('15:23:59');   -- null

1.13weekofyear

        当前时间是一年中的第几周

select weekofyear('2020-06-19 15:23:59');   -- 25
select weekofyear('2022-09-13');   -- 37

1.14dayofmonth

        当前时间是一个月中的第几天

select dayofmonth('2020-06-19 15:23:59');   -- 19
select dayofmonth('2022-09-13');   -- 13

1.15dayofweek

        当前时间是一周中的第几天

select `dayofweek`('2022-07-07 14:37:22');  -- 5
select `dayofweek`('2022-02-23');  -- 4

1.16months_betwee

        两个日期间的月份

select months_between('2022-10-23','2022-07-07');   -- 3.51612903
select months_between('2022-1-23','2022-07-07');    --- 5.48387097
select months_between('2022-12-23 19:37:22','2022-07-07 14:37:22');    --- 5.52284946

1.17add_months

        日期加减月

select add_months('2022-07-07',3);  -- 2022-10-07
select add_months('2022-07-07',-3);  -- 2022-04-07
select add_months('2022-07-07 14:37:22',-6);  -- 2022-01-07

1.18datediff

        两个日期相差的天数

select datediff('2022-12-23 19:37:22','2022-07-07 14:37:22');   -- 169
select datediff('2022-12-23','2022-07-07');   -- 169

1.19date_add

        日期加天数(正数则加、负数则减)

select date_add('2022-07-07',-3);   -- 2022-07-04
select date_add('2022-07-07 14:37:22',6);   -- 2022-07-13

1.20date_sub

        日期减天数(正数则减、负数则加)

select date_sub('2022-07-07',-3);   -- 2022-07-10
select date_sub('2022-07-07 14:37:22',6);   -- 2022-07-01

1.21last_day

        日期的当月的最后一天

select last_day('2022-07-07');  -- 2022-07-31
select last_day('2022-06-07 14:37:22');  -- 2022-06-30

1.22date_format

        格式化时间

select date_format('2022-06-07 14:37:22','yyyy/MM/dd HH:mm:ss');    -- 2022/06/07 14:37:22
select date_format('2022-06-07','yyyy/MM/dd');    -- 2022/06/07
select date_format('2022-06-07','yyyy*MM*dd');    -- 2022*06*07
select date_format('2022-06-07','yyyy_MM_dd');    -- 2022_06_07

2.常用取整函数(3个)

2.1round

        四舍五入(可指定保留整数或小数点后几位)

select round(6.28); -- 6
select round(6.58); -- 7
select round(6.58410834,2); -- 6.58
select round(6.58470834,3); -- 6.585
select round(6.5,3); -- 6.500

2.2ceil

        向上取整

select ceil(6.1);   -- 7
select ceil(9.001);   -- 10

2.3floor

        向下取整

select `floor`(9.999);  -- 9
select `floor`(7.8);  -- 7

3.常用字符串操作函数(8个)

3.1upper

        转大写

select upper('abcd');   -- ABCD
select upper('XxxYyyZzz');   -- XXXYYYZZZ

3.2lower

        转小写

select lower('ABCD');   -- abcd
select lower('XxxYyyZzz'); -- xxxyyyzzz

3.3length

        求长度

select length('dolphinscheduler');  -- 16
select length('aa bb');  -- 5
select length('aabb');  -- 4

3.4trim

        前后去空格

desc function extended trim;    -- trim(str) - Removes the leading and trailing space characters from str 
select trim('  aabb  ');    -- aabb
select trim('  aa bb  ');    -- aa bb

3.5lpad

        向左补齐,到指定长度

desc function extended lpad;    -- lpad(str, len, pad) - Returns str, left-padded with pad to a length of len
select lpad('abc',5,'0');   -- 00abc

3.6rpad

        向右补齐,到指定长度

desc function extended rpad;    -- rpad(str, len, pad) - Returns str, right-padded with pad to a length of len
select rpad('abc',5,'0');   -- abc00

3.7regexp_replace

        使用正则表达式匹配目标字符串,匹配成功后替换

select regexp_replace('2020-10-25','-','/');    -- 2020/10/25
select regexp_replace('aHbHcHdHe','H',' ');    -- a b c d e

3.8substring

        从指定位置开始截取字符串,角标从1开始数,正数则从左边开始,负数则从右边开始

select substring('字符串',从哪开始截[,截几个])
select substring('abcdefghijk',3);  -- cdefghijk
select substring('abcdefghijk',3,2);  -- cd
select substring('abcdefghijk',-3);  -- ijk
select substring('abcdefghijk',-3,2);  -- ij

4.集合操作(5个)

4.1size

        求集合中的元素个数

select size(`array`(1,2,3,4,5,6));  -- 6
desc function extended `map`;   -- map(key0, value0, key1, value1...) - Creates a map with the given key/value pairs 
select size(`map`("张三",1,"李四",23,"王五",15));  -- 3

4.2map_keys

        返回map中的key

select map_keys(`map`("张三",1,"李四",23,"王五",15)); -- ["张三","李四","王五"]

4.3map_values

        返回map中的value

select map_values(`map`("张三",1,"李四",23,"王五",15)); -- [1,23,15]

4.4array_contains

        判断array中是否包含某个元素

select array_contains(`array`(1,2,3,4,5,6),5);  -- true
select array_contains(`array`('a','b','c'),'d');  -- false

4.5sort_array

        将array中的元素排序

select sort_array(`array`(22,3,19,17,35,8));    -- [3,8,17,19,22,35]
select sort_array(`array`('zhang','li','wang'));    -- ["li","wang","zhang"]

5.其他常用内置函数(15个)

        测试表:test1

test1
name age
张三 12
李四   22
null 19
王五 35
赵六 null
null 27

5.1nvl

        给值为null的数据复制

select nvl(name,'no name') from test1;
/**
张三
李四
no name
王五
赵六
no name
*/

5.2case when then [when then] else end [别名]

        根据不同的数据,返回不同的值

select
    case name
        when '张三' then '张小三三'
        when '李四' then '李小四四'
        else name
    end name1
from test1
where name is not null;
/**
name1
张小三三
李小四四
王五
赵六
*/

5.3行转列(4个)

5.3.1concat

        返回输入字符串连接后的结果,支持任意个输入的字符串

select concat("123","aaa");	-- 123aaa
select concat("123","aaa",'张三'); -- 123aaa张三

5.3.2concat_ws

        指定分隔符拼接字符串或数组(只能是字符串数组)

select concat_ws('$','123','456','789');    -- 123$456$789
select concat_ws('-',split("2020/06/14",'/'));  -- 2020-06-14
select concat_ws('_','123',`array`('456','789'));   -- 123_456_789

5.3.3collect_set

        将某字段去重汇总,返回array类型字段

        测试表:test2

test2
name
tom
marry
peter
tom
tom
marry
select collect_set(name) from test2;    -- ["tom","marry","peter"]

5.3.4collect_list

        将某字段不去重汇总,返回array类型字段

select collect_list(name) from test2;   -- ["tom","marry","peter","tom","tom","marry"]

5.4列传行(3个)

5.4.1explode

        将一列复杂的array或map拆分成多行

        测试表:test3

category1 category2
["悬疑","动作","科幻","剧情","心理"] 北京,四川,广东,重庆,浙江
select
    name
from test3
lateral view explode(category1) tmp as name;
/**
name
悬疑
动作
科幻
剧情
心理
*/

5.4.2split

        按照regex字符串分割str,返回分割后的字符串数组

select
    split(category2,',') name
from test3;
/**
name
["北京","四川","广东","重庆","浙江"]
*/

5.4.3laterval view

        用法:laterval view UDTF(expression) 临时表名 as 列名

        和split、explode等UDTF一起使用,将一行数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。lateral view首先为原始表的每行调用UDTF,UDTF会把一行拆分成一行或者多行,lateral view再把结果组合,产生一个支持别名表的虚拟表。

select
    category2,
    name
from test3
lateral view explode(category1) tmp as name;
/**
category2	name
"北京,四川,广东,重庆,浙江"	悬疑
"北京,四川,广东,重庆,浙江"	动作
"北京,四川,广东,重庆,浙江"	科幻
"北京,四川,广东,重庆,浙江"	剧情
"北京,四川,广东,重庆,浙江"	心理
*/

5.5开窗函数

5.5.1定义及用法

        灵活运用窗口函数可以解决很多复杂的问题,如去重、排名、同比及和环比、连续登录等。

        用法:function(字段) over ([partition by 字段] [order by 字段] [])

function
解释 支持的函数
聚合函数 sum()、max()、min()、avg()等
排序函数 rank()、dens_rank()、row_number()、ntile()等
统计比较函数 lead()、lag()、first_value()等
over()
解释 指定分析函数工作的数据窗口大小,窗口会随着行的变化而变化
partition by 表示将数据先按字段分区
order by 表示将各个分区内的数据按字段进行排序

        1)如果不指定partition by,则不对数据进行分区,即所有数据看做一个分区

        2)如果不指定order by,则不对各分区进行排序,通常用于那些顺序无关的窗口函数,如sum()

window_expression
解释 窗口边界的设置
n preceding 往前n行
n following 往后n行
current row 当前行
unbounded preceding 从前面的起点开始
unbounded following 到后面的终点结束

        1)如果不指定开窗子句:

                ①前面没有指定order by,则默认使用分区内所有行,相当于

Function() over(rows between unbounded precedeing and unbounded following)

                ②前面指定了order by,则默认使用分区内的起点到当前行,相当于

Function() over(range between unbounded preceding and current row)

5.5.2range和rows

        range是逻辑窗口;rows是物理窗口

        测试表:id_test

id_test
id
1
1
3
6
6
6
7
8
9
select
    id,
    sum(id) over () no_order_sum,
    sum(id) over (order by id) default_sum,
    sum(id) over (order by id range between unbounded preceding and current row ) range_unbounded_sum,
    sum(id) over (order by id rows between unbounded preceding and current row ) rows_unbounded_sum,
    sum(id) over (order by id range between 1 preceding and 2 following) range_sum_1_2,
    sum(id) over (order by id range between 2 preceding and 1 following) range_sum_2_1,
    sum(id) over (order by id rows between 1 preceding and 2 following) rows_sum_1_2,
    sum(id) over (order by id rows between 2 preceding and 1 following) rows_sum_2_1
from test5;
/**
id	no_order_sum  default_sum  range_unbounded_sum rows_unbounded_sum range_sum_1_2	range_sum_2_1 rows_sum_1_2	rows_sum_2_1
1	    47	            2	                2				1				5			2			5				2
1		47				2					2				2				5			2			11				5
3		47				5					5				5				3			5			16				11
6		47				23					23				11				33			25			21				16
6		47				23					23				17				33			25			25				21
6		47				23					23				23				33			25			27				25
7		47				30					30				30				42			33			30				27
8		47				38					38				38				24			44			24				30
9		47				47					47				47				17			24			17				24
*/
/**
1.理解 no_order_sum :无partition by、order by,即无分区,无窗口大小限定:
id	no_order_sum
1		47	=1+1+3+6+6+6+7+8+9
1		47	=1+1+3+6+6+6+7+8+9
3		47	=1+1+3+6+6+6+7+8+9
6		47	...
6		47	...
6		47	...
7		47	...
8		47	...
9		47	...

2.理解 default_sum :无partition by,指定order by,未指定开窗子句,则默认range between unbounded preceding and current row,即第一行id值到当前行id值的范围内的值都要sum(注意这是逻辑区间,当前行不一定真的就只到当前行):
id	default_sum
1		2	=1+1	解释:第一行的id值为1,当前行的id值为1,即id在 1<=id<=1 这个区间内都要被sum,第二行的1也在这个范围,这叫逻辑区间
1		2	=1+1	解释:第一行的id值为1,当前行的id值为1,即id在 1<=id<=1 这个区间内都要被sum
3		5	=1+1+3	解释:第一行的id值为1,当前行的id值为3,即id在 1<=id<=3 这个区间内都要被sum
6		23	=1+1+3+6+6+6	解释:第一行的id值为1,当前行的id值为6,即id在 1<=id<=6 这个区间内都要被sum
6		23	=1+1+3+6+6+6	解释:第一行的id值为1,当前行的id值为6,即id在 1<=id<=6 这个区间内都要被sum
6		23	=1+1+3+6+6+6	解释:第一行的id值为1,当前行的id值为6,即id在 1<=id<=6 这个区间内都要被sum
7		30	=1+1+3+6+6+6+7	解释:第一行的id值为1,当前行的id值为7,即id在 1<=id<=7 这个区间内都要被sum
8		38	=1+1+3+6+6+6+7+8	解释:第一行的id值为1,当前行的id值为8,即id在 1<=id<=8 这个区间内都要被sum
9		47	=1+1+3+6+6+6+7+8+9	解释:第一行的id值为1,当前行的id值为9,即id在 1<=id<=9 这个区间内都要被sum

3.理解 range_unbounded_sum :属于第二种的默认情况,略

4.理解 rows_unbounded_sum :无partition by,指定order by、开窗子句rows between unbounded preceding and current row,即第一行到当前行(注意这是物理区间,当前行一定是当前行):
id	rows_unbounded_sum
1	1	=1	解释:从第一行加到当前行,当前是哪一行就加到这一行为止,这叫物理区间,下同
1	2	=1+1	解释:从第一行加到当前行
3	5	=1+1+3	解释:从第一行加到当前行
6	11	=1+1+3+6	解释:从第一行加到当前行
6	17	=1+1+3+6+6	解释:从第一行加到当前行
6	23	=1+1+3+6+6+6	解释:从第一行加到当前行
7	30	=1+1+3+6+6+6+7	解释:从第一行加到当前行
8	38	=1+1+3+6+6+6+7+8	解释:从第一行加到当前行
9	47	=1+1+3+6+6+6+7+8+9	解释:从第一行加到当前行

5.理解 range_sum_1_2 :无partition by,指定order by、开窗子句range between 1 preceding and 2 following,即id值的前一个值到id值的后两个值之间的值都要sum
id	range_sum_1_2
1	5	=1+1+3	解释:当前行的id值为1,根据开窗子句,id的范围在 1-1<=id<=1+3,即id为0、1、2、3的都要sum,显然当前行后有1、3要sum
1	5	=1+1+3	解释:当前行的id值为1,根据开窗子句,id的范围在 1-1<=id<=1+3,即id为0、1、2、3的都要sum,显然当前行前后有1、3要sum
3	3	=3	解释:当前行的id值为3,根据开窗子句,id的范围在 3-1<=id<=3+2,即id为2、3、4、5的都要sum,显然只有3,当前行前后没有2、4、5
6	33	=6+6+6+7+8	解释:当前行的id值为6,根据开窗子句,id的范围在 6-1<=id<=6+2,即id为5、6、7、8的都要sum,显然当前行后6、7、8要sum
6	33	=6+6+6+7+8	解释:当前行的id值为6,根据开窗子句,id的范围在 6-1<=id<=6+2,即id为5、6、7、8的都要sum,显然当前行前后6、7、8要sum
6	33	=6+6+6+7+8	解释:当前行的id值为6,根据开窗子句,id的范围在 6-1<=id<=6+2,即id为5、6、7、8的都要sum,显然当前行前后6、7、8要sum
7	42	=6+6+6+7+8+9  解释:当前行的id为7,根据开窗子句,id的范围在 7-1<=id<=7+2,即id为6、7、8、9的都要sum,显然当前行前后6、7、8、9要sum
8	24	=7+8+9	解释:当前行的id为8,根据开窗子句,id的范围在 8-1<=id<=8+2,即id为7、8、9、10的都要sum,显然当前行前后没有10
9	17	=8+9 解释:当前行的id为9,根据开窗子句,id的范围在 9-1<=id<=9+2,即id为8、9、10、11的都要sum,显然没有10、11

6.理解 range_sum_2_1 :无partition by,指定order by、开窗子句range between 2 preceding and 1 following,即id值的前两个值到id值的后一个值之间的值都要sum
id	range_sum_2_1
1	2	=1+1	解释:当前行的id值为1,根据开窗子句,id的范围在 1-2<=id<=1+1,即id为-1、0、1、2的都要sum,显然当前行前后没有-1、0、2
1	2	=1+1	解释:当前行的id值为1,根据开窗子句,id的范围在 1-2<=id<=1+1,即id为-1、0、1、2的都要sum,显然当前行前后没有-1、0、2
3	5	=1+1+3	解释:当前行的id值为3,根据开窗子句,id的范围在 3-2<=id<=3+1,即id为1、2、3、4的都要sum,显然当前行前后没有2、4
6	25	=6+6+6+7	解释:当前行的id值为6,根据开窗子句,id的范围在 6-2<=id<=6+1,即id为4、5、6、7的都要sum,显然当前行前后没有4、5
6	25	=6+6+6+7	解释:当前行的id值为6,根据开窗子句,id的范围在 6-2<=id<=6+1,即id为4、5、6、7的都要sum,显然当前行前后没有4、5
6	25	=6+6+6+7	解释:当前行的id值为6,根据开窗子句,id的范围在 6-2<=id<=6+1,即id为4、5、6、7的都要sum,显然当前行前后没有4、5
7	33	=6+6+6+7+8	解释:当前行的id值为7,根据开窗子句,id的范围在 7-2<=id<=7+1,即id为5、6、7、8的都要sum,显然当前行前后没有5
8	44	=6+6+6+7+8+9	解释:当前行的id值为8,根据开窗子句,id的范围在 8-2<=id<=8+1,即id为6、7、8、9的都要sum,显然当前行前后都有
9	24	=7+8+9	解释:当前行的id值为9,根据开窗子句,id的范围在 9-2<=id<=9+1,即id为7、8、9、10的都要sum,显然当前行前后没有10

7.理解 rows_sum_1_2 :无partition by,指定order by、开窗子句rows between 1 preceding and 2 following,即当前行的前一行到后两行都要sum
id	rows_sum_1_2
1	5	=1+1+3	解释:当前行前一行加到后两行
1	11	=1+1+3+6	解释:当前行前一行加到后两行
3	16	=1+3+6+6	解释:当前行前一行加到后两行
6	21	=3+6+6+6	...
6	25	=6+6+6+7	...
6	27	=6+6+7+8	...
7	30	=6+7+8+9	...
8	24	=7+8+9	...
9	17	=8+9	...

8.理解 rows_sum_2_1 :无partition by,指定order by、开窗子句rows between 2 preceding and 1 following,即当前行的前两行到后一行都要sum
di	rows_sum_2_1
1	2	=1+1	解释:当前行前两行加到后一行
1	5	=1+1+3	解释:当前行前两行加到后一行
3	11	=1+1+3+6	解释:当前行前两行加到后一行
6	16	=1+3+6+6	解释:当前行前两行加到后一行
6	21	=3+6+6+6	...
6	25	=6+6+6+7	...
7	27	=6+6+7+8	...
8	30	=6+7+8+9	...
9	24	=7+8+9	...
*/

        总结:

                range是逻辑分区,窗口大小与被统计的字段本身的值有关

                rows是物理分区,窗口大小与被统计的字段本身的值无关,仅仅是当前行在物理位置上上下移动多少

5.5.3实例

        测试表:test4

test4
name orderdate cost
jack 2017-01-01 10
tony 2017-01-02 15
jack 2017-02-03 23
tony 2017-01-04 29
jack 2017-01-05 46
jack 2017-04-06 42
tony 2017-01-07 50
jack 2017-01-08 55
mart 2017-04-08 62
mart 2017-04-09 68
neil 2017-05-10 12
mart 2017-04-11 75
neil 2017-06-12 80
mart 2017-04-13 94

        1)需求一:查询在2017年4月份购买过的顾客及总人数

-- 找出2017-04购买过的所有顾客
select
    name
from test4
where substring(orderdate,1,7) = '2017-04';
/**
name
jack
mart
mart
mart
mart
*/
-- 正确sql:
select
    name,
    count(name) over () count_name
from test4
where substring(orderdate,1,7) = '2017-04'
group by name;
-- 理解:按name分组后对count函数开窗,未指定partition by,则将分组后的整个数据看做一个分区
/**
name	count_name
mart	2
jack	2
*/

-- =====================================================================================================================
-- 观察下列写法:
select
    name,
    count(name) count_name
from test4
where substring(orderdate,1,7) = '2017-04'
group by name;
-- 理解:按name分组后不开窗,则组内统计
/**
name	count_name
mart	4
jack	1
*/

-- =========================================================================================
-- 观察下列写法:
select
    name,
    count(name) over (partition by name) count_name
from test4
where substring(orderdate,1,7) = '2017-04'
group by name;
-- 理解:按name分组后对count函数开窗,指定partition by,则在每个分区内统计
/**
name	count_name
mart	1
jack	1
*/

-- =========================================================================================
-- 观察下列写法:
select
    name,
    count(name) over (partition by name) count_name
from test4
where substring(orderdate,1,7) = '2017-04';
-- 理解:未分组对count函数开窗,指定partition by,则在每个分区内统计
/**
name	count_name
mart	4
mart	4
mart	4
mart	4
jack	1
*/

        2)需求二:查询顾客的购买明细及月购买金额

select
    name,
    orderdate,
    cost,
    sum(cost) over (partition by name,date_format(orderdate,'yyyy-MM')) sum_cost
from test4;
-- 理解:未分组(即使分组也不改变结果,因为每一行都不一样,分组不会改变原数据)对sum函数开窗,并指定partition by,按照name,年-月一分区求sum
/**
name	orderdate	cost	sum_cost
jack	2017-01-01	10	111
jack	2017-01-05	46	111
jack	2017-01-08	55	111
tony	2017-01-02	15	94
tony	2017-01-04	29	94
tony	2017-01-07	50	94
jack	2017-02-03	23	23
jack	2017-04-06	42	42
mart	2017-04-08	62	299
mart	2017-04-09	68	299
mart	2017-04-11	75	299
mart	2017-04-13	94	299
neil	2017-05-10	12	12
neil	2017-06-12	80	80
*/

        3)需求三:将每个顾客的cost按照日期进行累加

-- 写法一:
select
    name,
    orderdate,
    cost,
    sum(cost) over(partition by name order by orderdate) sum_cost
from test4;
-- 理解:指定order by,未指定窗口边界,则默认从分区内的起点的值到当前行的值的范围内
/**
name	orderdate	cost	sum_cost
jack	2017-01-01	10	10	=10
jack	2017-01-05	46	56	=10+46
jack	2017-01-08	55	111	=10+46+55
jack	2017-02-03	23	134	=10+46+55+23
jack	2017-04-06	42	176	=10+46+55+23+42

neil	2017-05-10	12	12	=12
neil	2017-06-12	80	92	=12+80

tony	2017-01-02	15	15	=15
tony	2017-01-04	29	44	=15+29
tony	2017-01-07	50	94	=15+29+50

mart	2017-04-08	62	62	=62
mart	2017-04-09	68	130	=62+68
mart	2017-04-11	75	205	=62+68+75
mart	2017-04-13	94	299	=62+68+75+94
观察窗口函数是如何运行的
先划定分区(partition by),再排序(order by),随着排好的顺序一次往下sum
*/

-- 写法二
select
    name,
    orderdate,
    cost,
    sum(cost) over(partition by name order by orderdate rows between unbounded preceding and current row) sum_cost
from test4;
-- 理解:指定order by,且指定窗口边界:从前面的起点到当前行,则按照指定的边界进行sum
/**
name	orderdate	cost	sum_cost
jack	2017-01-01	10	10
jack	2017-01-05	46	56
jack	2017-01-08	55	111
jack	2017-02-03	23	134
jack	2017-04-06	42	176
neil	2017-05-10	12	12
neil	2017-06-12	80	92
tony	2017-01-02	15	15
tony	2017-01-04	29	44
tony	2017-01-07	50	94
mart	2017-04-08	62	62
mart	2017-04-09	68	130
mart	2017-04-11	75	205
mart	2017-04-13	94	299

*/

-- =========================================================================================
-- 观察下列写法:
select
    name,
    orderdate,
    cost,
    sum(cost) over(partition by name order by orderdate rows between 1 preceding and current row) sum_cost
from test4;
/**
name	orderdate	cost	sum_cost
jack	2017-01-01	10	10	=10
jack	2017-01-05	46	56	=10+46
jack	2017-01-08	55	101	=46+55
jack	2017-02-03	23	78	=55+23
jack	2017-04-06	42	65	=23+42

neil	2017-05-10	12	12	=12
neil	2017-06-12	80	92	=12+80

tony	2017-01-02	15	15	=15
tony	2017-01-04	29	44	=15+29
tony	2017-01-07	50	79	=29+50

mart	2017-04-08	62	62	=62
mart	2017-04-09	68	130	=62+68
mart	2017-04-11	75	143	=68+75
mart	2017-04-13	94	169	=75+94
*/
-- 理解:指定order by,且指定窗口边界:从前一行到当前行,则按照指定的边界进行sum

-- =========================================================================================
-- 观察下列写法:
select
    name,
    orderdate,
    cost,
    sum(cost) over(partition by name order by orderdate rows between 1 preceding and 1 following) sum_cost
from test4;
/**
name	orderdate	cost	sum_cost
jack	2017-01-01	10	56	=10+46
jack	2017-01-05	46	111	=10+46+55
jack	2017-01-08	55	124	=46+55+23
jack	2017-02-03	23	120	=55+23+42
jack	2017-04-06	42	65	=23+42

neil	2017-05-10	12	92	=12+80
neil	2017-06-12	80	92	=12+80

tony	2017-01-02	15	44	=15+29
tony	2017-01-04	29	94	=15+29+50
tony	2017-01-07	50	79	=29+50

mart	2017-04-08	62	130	=62+68
mart	2017-04-09	68	205	=62+68+75
mart	2017-04-11	75	237	=68+75+94
mart	2017-04-13	94	169	=75+94
*/
-- 理解:指定order by,且指定窗口边界:从前一行到下一行,则按照指定的边界进行sum
-- 体会窗口边界的用法

        4)需求:四:查看顾客上次的购买时间(lag)

                lag()函数:lag(列名[,往上几行] [,默认值])

                用于统计窗口内往上第几行的值,取不到时(如第一行,往上没有)给默认值

select
    name,
    orderdate,
    lag(orderdate,1,'1970-01-01') over (partition by name order by orderdate) last_time
from test4;
/**
name	orderdate	last_time
jack	2017-01-01	1970-01-01	=默认值
jack	2017-01-05	2017-01-01	=上一行数据
jack	2017-01-08	2017-01-05	=上一行数据
jack	2017-02-03	2017-01-08	=上一行数据
jack	2017-04-06	2017-02-03	=上一行数据

neil	2017-05-10	1970-01-01	=默认值
neil	2017-06-12	2017-05-10	=上一行数据

tony	2017-01-02	1970-01-01	=默认值
tony	2017-01-04	2017-01-02	=上一行数据
tony	2017-01-07	2017-01-04	=上一行数据

mart	2017-04-08	1970-01-01	=默认值
mart	2017-04-09	2017-04-08	=上一行数据
mart	2017-04-11	2017-04-09	=上一行数据
mart	2017-04-13	2017-04-11	=上一行数据
*/
-- 理解:对orderdate字段:统计窗口内往上1行的数据,取不到时给定默认值1970-01-01

-- =========================================================================================
-- 观察下列写法:
select
    name,
    orderdate,
    lag(orderdate,2,'1970-01-01') over (partition by name order by orderdate) last_time
from test4;
/**
name	orderdate	last_time
jack	2017-01-01	1970-01-01	=默认值
jack	2017-01-05	1970-01-01	=默认值
jack	2017-01-08	2017-01-01	=往上二行的数据
jack	2017-02-03	2017-01-05	=往上二行的数据
jack	2017-04-06	2017-01-08	=往上二行的数据

neil	2017-05-10	1970-01-01	=默认值
neil	2017-06-12	1970-01-01	=默认值

tony	2017-01-02	1970-01-01	=默认值
tony	2017-01-04	1970-01-01	=默认值
tony	2017-01-07	2017-01-02	=往上二行的数据

mart	2017-04-08	1970-01-01	=默认值
mart	2017-04-09	1970-01-01	=默认值
mart	2017-04-11	2017-04-08	=往上二行的数据
mart	2017-04-13	2017-04-09	=往上二行的数据
*/
-- 理解:对orderdate字段:统计窗口内往上2行的数据,取不到时给定默认值1970-01-01

        5)需求五:查看顾客下次的购买时间(lead)

                lead()函数:lead(列名[,往上几行] [,默认值])

                用于统计窗口内往下第几行的值,取不到时(如最后一行行,往下没有)给默认值

                与lag()函数正好相反

select
    name,
    orderdate,
    lead(orderdate,1,'9999-12-31') over (partition by name order by orderdate) next_time
from test4;
/**
name	orderdate	next_time
jack	2017-01-01	2017-01-05	=下一行数据
jack	2017-01-05	2017-01-08	=下一行数据
jack	2017-01-08	2017-02-03	=下一行数据
jack	2017-02-03	2017-04-06	=下一行数据
jack	2017-04-06	9999-12-31	=默认值

neil	2017-05-10	2017-06-12	=下一行数据
neil	2017-06-12	9999-12-31	=默认值

tony	2017-01-02	2017-01-04	=下一行数据
tony	2017-01-04	2017-01-07	=下一行数据
tony	2017-01-07	9999-12-31	=默认值

mart	2017-04-08	2017-04-09	=下一行数据
mart	2017-04-09	2017-04-11	=下一行数据
mart	2017-04-11	2017-04-13	=下一行数据
mart	2017-04-13	9999-12-31	=默认值
*/
-- 理解:对orderdate字段:统计窗口内往下1行的数据,取不到时给定默认值9999-12-31

-- =========================================================================================
-- 观察下列写法:
select
    name,
    orderdate,
    lead(orderdate,2,'9999-12-31') over (partition by name order by orderdate) next_time
from test4;
/**
name	orderdate	next_time
jack	2017-01-01	2017-01-08	=往下第2行的数据
jack	2017-01-05	2017-02-03	=往下第2行的数据
jack	2017-01-08	2017-04-06	=往下第2行的数据
jack	2017-02-03	9999-12-31	=默认值
jack	2017-04-06	9999-12-31	=默认值

neil	2017-05-10	9999-12-31	=默认值
neil	2017-06-12	9999-12-31	=默认值

tony	2017-01-02	2017-01-07	=往下第2行的数据
tony	2017-01-04	9999-12-31	=默认值
tony	2017-01-07	9999-12-31	=默认值

mart	2017-04-08	2017-04-11	=往下第2行的数据
mart	2017-04-09	2017-04-13	=往下第2行的数据
mart	2017-04-11	9999-12-31	=默认值
mart	2017-04-13	9999-12-31	=默认值
*/
-- 理解:对orderdate字段:统计窗口内往下第2行的数据,取不到时给定默认值9999-12-31

        6)需求六:查询前20%时间的订单信息(ntile)

                ntile()函数:ntile(数值)

                为已排序的行,均分为指定数量的组,组号按顺序排列,返回组号,不支持rows between

/**
分析:表中一共14行数据,前20%大约是14*0.2≈3行数据,但是数据每天都在变化,某一天变成29行数据,前20%大约是29*0.2≈6行,如果仅仅是简单的排序然后limit 3就写死了,数据变化后统计出来的数据就不再是前20%了,此时就要用到ntile函数,20%即1/5,name将排好序的数据均分成5份,再作为子查询,取序号为1的。数据变化变多时,每一份数据也跟着变多
*/
select
    name,
    orderdate,
    cost,
    ntile(5) over (order by orderdate) sort_
from test4;
/**
name	orderdate	cost	sort_
jack	2017-01-01	10	1
tony	2017-01-02	15	1
tony	2017-01-04	29	1
jack	2017-01-05	46	2
tony	2017-01-07	50	2
jack	2017-01-08	55	2
jack	2017-02-03	23	3
jack	2017-04-06	42	3
mart	2017-04-08	62	3
mart	2017-04-09	68	4
mart	2017-04-11	75	4
mart	2017-04-13	94	4
neil	2017-05-10	12	5
neil	2017-06-12	80	5
*/
-- 返回前20%即sort_=1
select
    name,
    orderdate,
    cost
from (select name,
             orderdate,
             cost,
             ntile(5) over (order by orderdate) sort_
      from test4) t1
where sort_=1;
/**
name	orderdate	cost
jack	2017-01-01	10
tony	2017-01-02	15
tony	2017-01-04	29
*/

5.6排名函数(3个)

5.6.1rank

        排名相同时会重复,总数不变

select
    orderdate,
    rank() over (order by orderdate) rk
from test4
/**
orderdate	rk
2017-01-01	1	
2017-01-02	2
2017-01-02	2
2017-01-05	4
2017-01-07	5
*/
-- 理解:rank函数需要窗口内数据有序,所以指定order by

5.6.2dens_rank

         排名相同时会重复,总数减少

select
    orderdate,
    dense_rank() over (order by orderdate) dr
from test4;
/**
orderdate	dr
2017-01-01	1	
2017-01-02	2
2017-01-02	2
2017-01-05	3
2017-01-07	4
*/
-- 理解:dense_rank函数需要窗口内数据有序,所以指定order by

5.6.3row_number

        排名相同时忽略重复,按顺序排

select
    orderdate,
    row_number() over (order by orderdate) rn
from test4;
/**
orderdate	rn
2017-01-01	1	
2017-01-02	2
2017-01-02	3
2017-01-05	4
2017-01-07	5
*/
-- 理解:row_number函数需要窗口内数据有序,所以指定order by

5.7first_value

        first_value(字段,true/false),对某字段取窗口内第一个值,ture表示忽略null值,false表示不忽略null值

        测试表:test5

test5
id dt point
1001 2022-05-01 null
1001 2022-05-02 null
1001 2022-05-01 null
1001 2022-05-01 张三_01
1001 2022-05-03 李四_02
1002 2022-05-04 null
1002 2022-05-01 null
1002 2022-05-05 王五_03
1003 2022-05-01 null
1003 2022-05-06 null
1003 2022-05-06 null
1003 2022-05-07 赵六_04
select
    id,
    dt,
    point,
    first_value(point,true) over (partition by id order by dt rows between unbounded preceding and unbounded following)
from test5;
/**
id			dt		point	first_point
1001	2022-05-01	null	 张三_01	 解释:1001分区内所有数据第一个值:“张三_01”
1001	2022-05-01	null	 张三_01	 解释:1001分区内所有数据第一个值:“张三_01”
1001	2022-05-01	张三_01	张三_01	解释:1001分区内所有数据第一个值:“张三_01”
1001	2022-05-02	null	 张三_01	 解释:1001分区内所有数据第一个值:“张三_01”
1001	2022-05-03	李四_02	张三_01	解释:1001分区内所有数据第一个值:“张三_01”

1002	2022-05-01	null	 王五_03	 解释:1002分区内所有数据第一个值:“王五_03”
1002	2022-05-04	null	 王五_03	 解释:1002分区内所有数据第一个值:“王五_03”
1002	2022-05-05	王五_03	王五_03	解释:1002分区内所有数据第一个值:“王五_03”

1003	2022-05-01	null	 赵六_04	 解释:1003分区内所有数据第一个值:“赵六_04”
1003	2022-05-06	null	 赵六_04	 解释:1003分区内所有数据第一个值:“赵六_04”
1003	2022-05-06	null	 赵六_04	 解释:1003分区内所有数据第一个值:“赵六_04”
1003	2022-05-07	赵六_04	赵六_04	解释:1003分区内所有数据第一个值:“赵六_04”
*/
-- -- 理解:对point取first_value,开窗指定partition by、order by,窗口子句rows between unbounded preceding and unbounded following,即窗口大小为分区内、排序后的整个数据

5.8last_value

         last_value(字段,true/false),对某字段取窗口内最后一个值,ture表示忽略null值,false表示不忽略null值

        测试表:test6

test6
id dt point
1001 2022-05-01 张三_01
1001 2022-05-02 null
1001 2022-05-01 null
1001 2022-05-01 null
1001 2022-05-03 李四_02
1002 2022-05-04 null
1002 2022-05-01 王五_03
1002 2022-05-05 null
1003 2022-05-01 赵六_04
1003 2022-05-06 null
1003 2022-05-06 null
1003 2022-05-07 null
select
    id,
    dt,
    point,
    last_value(point,true) over (partition by id order by dt) last_point
from test6;
/**
id			dt		point	last_point
1001	2022-05-01	张三_01	张三_01	解释:由于是range,窗口为第一行到第三行,忽略null值,则该窗口内最后一个值:“张三_01”
1001	2022-05-01	null	 张三_01	 解释:...,窗口为分区内第一行到第三行,忽略null值,则该窗口内最后一个值:“张三_01”
1001	2022-05-01	null	 张三_01	 解释:...,窗口为分区内第一行到第三行,忽略null值,则该窗口内最后一个值:“张三_01”
1001	2022-05-02	null	 张三_01	 解释:...,窗口为分区内第一行到第四行,忽略null值,则该窗口内最后一个值:“张三_01”
1001	2022-05-03	李四_02	李四_02	解释:...,窗口为分区内第一行到第五行,忽略null值,则该窗口内第一个值:“张三_01”,最后一个值:“李四_02”

1002	2022-05-01	王五_03	王五_03	解释:...,窗口为分区内第一行到第一行,忽略null值,则该窗口内最后一个值:“王五_03”
1002	2022-05-04	null	 王五_03	 解释:...,窗口为分区内第一行到第二行,忽略null值,则该窗口内最后一个值:“王五_03”
1002	2022-05-05	null	 王五_03	 解释:...,窗口为分区内第一行到第三行,忽略null值,则该窗口内最后一个值:“王五_03”

1003	2022-05-01	赵六_04	赵六_04	解释:...,窗口为分区内第一行到第一行,忽略null值,则该窗口内最后一个值:“赵六_04”
1003	2022-05-06	null	 赵六_04	 解释:...,窗口为分区内第一行到第二行,忽略null值,则该窗口内最后一个值:“赵六_04”
1003	2022-05-06	null	 赵六_04	 解释:...,窗口为分区内第一行到第三行,忽略null值,则该窗口内最后一个值:“赵六_04”
1003	2022-05-07	null	 赵六_04	 解释:...,窗口为分区内第一行到第四行,忽略null值,则该窗口内最后一个值:“赵六_04”
*/
-- 理解:对point取last_value,开窗指定partition by、order by,窗口子句默认range between unbounded preceding and current row

        实例表:game_user

game_user
id dt
1001 2022-05-01 23:21:33
1003 2022-05-02 23:21:33
1002 2022-05-01 23:21:33
1003 2022-05-01 23:21:33
1001 2022-05-03 23:21:33
1003 2022-05-04 23:21:33
1002 2022-05-01 23:21:33
1001 2022-05-05 23:21:33
1001 2022-05-01 23:21:33
1002 2022-05-06 23:21:33
1001 2022-05-06 23:21:33
1001 2022-05-07 23:21:33

        需求:求每个用户的最大连续登录次数,断一天也算连续,如1、3、5则视为连续5天登录

-- 思路:
/**
		  1001	2022-05-01	null			 1001	2022-05-01	1001_2022-05-01			1001	2022-05-01	1001_2022-05-01
		  1001	2022-05-03	2022-05-01		 1001	2022-05-03	null					1001	2022-05-03	1001_2022-05-01
		  1001	2022-05-05	2022-05-03		 1001	2022-05-05	null					1001	2022-05-05	1001_2022-05-01
	 lag  1001	2022-05-06	2022-05-05  if	 1001	2022-05-06	null		last_value  1001	2022-05-06	1001_2022-05-01
原数据===> 1001  2022-05-07  2022-05-06 ====> 1001	2022-05-07  null			  =====>  1001	  2022-05-07  1001_2022-05-01
		  1002	2022-05-01	null			 1002	2022-05-01	1002_2022-05-01			1002	2022-05-01	1002_2022-05-01
		  1002	2022-05-06	2022-05-01		 1002	2022-05-06	1002_2022-05-06			1002	2022-05-06	1002_2022-05-06
		  1003	2022-05-01	null			 1003	2022-05-01	1003_2022-05-01			1003	2022-05-01	1003_2022-05-01
		  1003	2022-05-02	2022-05-01		 1003	2022-05-01	null					1003	2022-05-01	1003_2022-05-01
		  1003	2022-05-04	2022-05-02		 1003	2022-05-04	null					1003	2022-05-04	1003_2022-05-01
在分组找出最大连续登录,取最大值
*/

select
    id,
    max(lianxu) max_login
from (select id,
             datediff(max(dt), min(dt)) + 1 lianxu
      from (select id,
                   dt,
                   last_value(point, true) over (partition by id order by dt) last_point
            from (select id,
                         dt,
                         `if`(datediff(dt, before_day) > 2 or before_day is null, concat(id, '_', dt), null) point
                  from (select id,
                               date_format(dt, 'yyyy-MM-dd')                                            dt,
                               lag(date_format(dt, 'yyyy-MM-dd'), 1) over (partition by id order by dt) before_day
                        from game_user
                        group by id, dt) t1) t2) t3
      group by id, last_point) t4
group by id;
/**
id	max_login
1001	7
1002	1
1003	4
*/

6.自定义函数

6.1函数分类

        1)UDF:一进一出

        2)UDAF:聚合函数:多进一出,如:count、sum...

        3)炸裂函数:一进多出,如:explode...

6.2实现步骤

        1)继承hive提供的类

org.apache.hadoop.hive.ql.udf.generic.GenericUDF  
org.apache.hadoop.hive.ql.udf.generic.GenericUDTF

         2)实现类中的抽象方法

         3)在hive命令行窗口创建函数

6.3引入自定义函数步骤

        1)添加jar包

hive(default)> add jar linux_jar_path;

        2)创建function

 hive(default)> create [temporary] function [dbname.]function_name AS class_name;

         3)删除自定义函数

hive(default)> drop [temporary] function [if exists] [dbname.]function_name;

6.4自定义UDF函数

6.4.1需求

        自定义一个UDF函数实现计算给定字符串的长度:

hive(default)> select my_len("abcd");
ok
4

 6.4.2实现

        1)创建一个Maven工程Hive

        2)在工程项目的pom.xml文件中导入依赖


		
			org.apache.hive
			hive-exec
			3.1.2
		

        3)创建一个类

package com.atguigu.hive;

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;

/**
 * 自定义UDF函数,需要继承GenericUDF类
 * 需求: 计算指定字符串的长度
 */
public class MyStringLength extends GenericUDF {
    /**
     *
     * @param arguments 输入参数类型的鉴别器对象
     * @return 返回值类型的鉴别器对象
     * @throws UDFArgumentException
     */
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
        // 判断输入参数的个数
        if(arguments.length !=1){
            throw new UDFArgumentLengthException("Input Args Length Error!!!");
        }
        // 判断输入参数的类型
        if(!arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
            throw new UDFArgumentTypeException(0,"Input Args Type Error!!!");
        }
        //函数本身返回值为int,需要返回int类型的鉴别器对象
        return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
    }

    /**
     * 函数的逻辑处理
     * @param arguments 输入的参数
     * @return 返回值
     * @throws HiveException
     */
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {
       if(arguments[0].get() == null){
           return 0 ;
       }
       return arguments[0].get().toString().length();
    }

    @Override
    public String getDisplayString(String[] children) {
        return "";
    }
}

        4)打成jar包上传到服务器/opt/module/hive/datas/myudf.jar

        5)将jar包添加到hive的classpath

hive (default)> add jar /opt/module/hive/datas/myudf.jar;

6.4.3创建临时函数 

        1)创建临时函数与开发好的java class关联

hive (default)> create temporary function my_len as "com.atguigu.hive. MyStringLength";

         2)在hql中使用自定义的函数

hive (default)> select ename,my_len(ename) len_word ename_len from emp;
OK
ename   len_word
fanfan  6
SMITH   5
ALLEN   5
WARD    4
JONES   5
MARTIN  6
BLAKE   5
CLARK   5
SCOTT   5
KING    4
TURNER  6
ADAMS   5
JAMES   5
FORD    4
MILLER  6

        注意:临时函数只跟会话有关系,跟库没有关系,只要创建临时函数的会话不断,再当前会话下,任意一个库都可以使用,其他会话全部不能使用

6.4.4创建永久函数

        1)创建

hive (default)> 
create function my_len2 
as "com.atguigu.hive.udf.MyUDF" 
using jar "hdfs://hadoop102:8020/udf/myudf.jar";

        2)使用

hive (default)> 
select 
    ename,
    my_len2(ename) ename_len 
from emp;

        3)删除

hive (default)> drop function my_len2;

        注意:永久函数跟会话没有关系,创建函数的会话断了以后,其他会话也可以使用。

​                   永久函数创建的时候,在函数名之前需要自己加上库名,如果不指定库名的话,会默认把当前库的库名给加上。

​                   永久函数使用的时候,需要在指定的库里面操作,或者在其他库里面使用的话加上 库名.函数名。

你可能感兴趣的:(Hive,大数据,hive,sql)