Hive-空字段赋值,时间类函数,分支函数,字符串拼接,COLLECT_SET(),EXPLODE(),窗口函数

空字段赋值NVL

    NVL:给值为NULL的数据赋值,格式:NVL(string, replace_with)。如果string为NULL,则返回replace_with的值(可以为字段名),否则返回string。如果两个参数都为NULL,则返回NULL。

select nvl(salary,0) from employee;

原数据		|		结果
161			|		161
949			|		946
NULL		|		0
852			|		852
NULL		|		0

时间类

  • date_format:格式化时间。日期必须是xxxx-xx-xx的形式(用“-”连接),时间必须是xx:xx:xx的形式。
select date_format('2020-6-1','yyyy-MM-dd HH:mm:ss');

2020-06-01 00:00:00
  • date_add / date_sub:时间与天数相加 / 相减
select date_add('2020-05-31',1);	2020-06-01
select date_add('2020-01-01',-1);	2019-12-31
  • datediff:计算相差的天数(前减后)
select datediff('2020-05-31','2021-01-01');		-215
  • from_unixtime()unix_timestamp():通常这两个结合使用,获取当前时间。不过这两个方法已被弃用,使用current_timestamp()
SELECT from_unixtime(unix_timestamp());

2020-08-22 12:42:09
  • current_timestamp():获取当前时间
select current_timestamp();

2020-08-22 12:44:03.575
  • current_date():获取当前日期
select current_date();

2020-08-22
  • 一系列时间函数:
select
	year(current_timestamp()),
	month(current_timestamp()),
	day(current_timestamp()),
	hour(current_timestamp()),
	minute(current_timestamp()),
	second(current_timestamp());.

_c0     _c1     _c2     _c3     _c4     _c5
2020    8       22      12      48      2
  • dayofmonth,dayofweek,weekofyear,
select dayofmonth(current_timestamp());	22
select dayofweek(current_timestamp());	7(星期日是第一天)
select weekofyear(current_date);	34

Hive-获取本月的第一天,本月的最后一天,本月的天数

字符串类

  • regexp_replace(string1,string2,replace_with):替换,将string1中的string2替换为replace_with。
select regexp_replace('2020/5/31','/','-');	2020-5-31

替换空白字符
regexp_replace(key, '\n|\t|\r', '') keyword
regexp_replace(key, '\\s+', '')
  • split(string,regex):第二个参数是正则表达式,按正则表达式切割字符串
select split('oneAtwoBthreeC','[ABC]');

["one","two","three"]
  • substr(string,startindex,length):从startindex开始,切割长度为length的字符串
select substr('2020-05-31',1,7);	2020-05

分支函数

    需求:假设有部门表dep,现要计算各个部门的男女人数

create table dep(
	name string,
	dept string,
	gender string)
row format delimited fields terminated by '\t';
name	dept    gender
Jack	A      	male
Rose	B      	female
Diana	A   	female
Lily	B      	female
Mike	B      	male
Bob		B      	male
Lucy	A      	female
Harry	B      	male
Rachel	B      	female

    结果如下:

dept    male	female
A       1       2
B       3       3
select
    dept,
    sum(case gender when 'male' then 1 else 0 end) male,
    sum(case gender when 'female' then 1 else 0 end) female
from
    dep
group by
    dept;

    或者使用if

select
    dept,
    sum(if(gender='male',1,0)) male,
    sum(if(gender='female',1,0)) female
from
    dep
group by
    dept;

字符串拼接

  • concat(string1,string2…):将任意个字符串(可以是字段名)拼接成一个字符串
select concat('A','-','B','-','C');		A-B-C
  • concat_ws(separator,string1,string2…):第一个参数是分隔符,后面的每个字符串均用分隔符拼接在一起。如果分隔符为NULL,返回值也为NULL。该函数会跳过分隔符后面为NULL的参数
select concat_ws('-','A','B','C');		A-B-C
select concat_ws(NULL,'A','B');			NULL
select concat_ws('-','A','B',NULL,'C');	A-B-C

COLLECT_SET(column)

    功能:该函数是聚合函数,将某字段的值汇总成一行,产生array类型字段(多行转一行)。
    需求:根据部门表,统计每个部门的男生和女生各有哪些人。

dept    gender    list
A       female	  Diana,Lucy
A       male      Jack
B       female    Rose,Lily,Rachel
B       male      Mike,Bob,Harry
select
	dept,gender,
	concat_ws(',',collect_set(name)) list
from
	dep
group by
	dept,gender;

EXPLODE(column)

    功能:将一列中复杂的arraymap类型才分成多行(一行转多行)。
    用法:lateral view explode(column) 表别名 as 列别名。将一列数据拆成多行数据,在此基础上对拆分的数据和原表进行关联。单独的explode无法与其它字段一起显示,需要lateral view。
    需求:根据电影表统计每种电影类型都包含哪些电影。

create table movie(
	name string,
	category array<string>)
row format delimited fields terminated by '\t'
collection items terminated by ',';
《肖申克的救赎》		犯罪,剧情
《霸王别姬》			剧情,爱情
《怦然心动》			爱情,喜剧
《喜剧之王》			喜剧,剧情,爱情
《蝙蝠侠:黑暗骑士》	动作,科幻
《功夫》				动作,喜剧
《盗梦空间》			科幻,悬疑
《星际穿越》			科幻,冒险
《阿凡达》			动作,科幻,冒险
《泰坦尼克号》		剧情,爱情,灾难
《赌神》				喜剧,剧情

    结果如下:

type    list
冒险    《星际穿越》,《阿凡达》
剧情    《肖申克的救赎》,《霸王别姬》,《喜剧之王》,《泰坦尼克号》,《赌神》
动作    《蝙蝠侠:黑暗骑士》,《功夫》,《阿凡达》
喜剧    《怦然心动》,《喜剧之王》,《功夫》,《赌神》
悬疑    《盗梦空间》
灾难    《泰坦尼克号》
爱情    《霸王别姬》,《怦然心动》,《喜剧之王》,《泰坦尼克号》
犯罪    《肖申克的救赎》
科幻    《蝙蝠侠:黑暗骑士》,《盗梦空间》,《星际穿越》,《阿凡达》
select
	type,
	concat_ws(',',collect_set(name)) list
from (
select
	name,type
from
	movie
	lateral view explode(category) t1 as type) t2
group by
	type;

JSON函数

  • get_json_object:用来解析json字符串的一个字段,可获取深层字段
    函数第一个参数填写字段,第二个参数使用 $ 表示json变量标识,然后用 .[] 读取JSON内的对象或数组
{"req":{"num":"123","user_info":{"city":"中国|广西|桂林"}}}
{"req":{"num":"456","user_info":{"city":"中国|湖南|长沙"}}}
{"req":{"num":"789","user_info":{"city":"中国|湖北|武汉"}}}
SELECT
  get_json_object(data,'$.req') col1,
  get_json_object(data,'$.req.num') col2,
  get_json_object(data,'$.req.user_info.city') col3
FROM
  table1
WHERE
  dt = get_date(-1)

运行结果:
“num”:“123”,“user_info”:{“city”:“中国|广西|桂林”}    123    “中国|广西|桂林”
“num”:“456”,“user_info”:{“city”:“中国|湖南|长沙”}    456    “中国|湖南|长沙”
“num”:“789”,“user_info”:{“city”:“中国|湖北|武汉”}    789    “中国|湖北|武汉”

  • json_tuple:用来解析json字符串的多个字段(不知道能不能深层获取)
    语法:lateral view json_tuple(JSON字段,字段1,字段2…) 表名 as 字段名1,字段名2…
{"key":"A","user_id":"Mark","app_name":"android"}
{"key":"B","user_id":"Jack","app_name":"android"}
{"key":"C","user_id":"Dack","app_name":"android"}
SELECT
  table2.key,
  table2.user_id,
  table2.app_name
FROM
  	table1
  	lateral view json_tuple(data,'key','user_id','app_name') table2 as
  	key,user_id,app_name
WHERE
  dt = get_date(-1)

运行结果:
A    Mark    android
B    Jack    android
C    Dack    android

    可以搭配get_json_object使用,来获得深层的JSON字段:

lateral view json_tuple(get_json_object(data,'$.req.user_info), city) t1 as city

窗口函数

    注意:窗口函数都是最后一步执行,而且仅位于order by字句之前。
    over():开窗,指定聚合(分析)函数工作时每一行(仅针对本行)的数据集的窗口大小,如果没有参数,则窗口大小为整个数据集。
    开窗为前面的聚合函数提供一个计算的窗口或数据区间,聚合函数只在这个指定的窗口里运算。
    每一条数据都是开一个窗口,这个窗口的大小根据over中的条件限定,然后在这个窗口的大小范围内执行聚合函数。
    开窗就是对结果再进行聚合运算,开窗是针对每一条结果,计算聚合函数的时候会有专门的数据集,数据集的多少取决于over的参数。
    下面有表记录了客户的购买明细:

create table business(
	name string,
	orderdate string,
	cost int)
row format delimited fields terminated by ',';
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
Rose,2017-04-08,62
Rose,2017-04-09,68
Mike,2017-05-10,12
Rose,2017-04-11,75
Mike,2017-06-12,80
Rose,2017-04-13,94

    分别执行下面的SQL语句。

select name,count(*) from business group by name;
select name,count(*) over() from business group by name;

    对于语句1,其含义是每个用户的购买次数,比如Jack购买了5次,Mike购买了2次。
    对于语句2,其没什么特殊含义,一般需要在over()里面传参数才有意义。这里讲一下为什么会得到这样的结果。把count(*) over()看成一个整体,在select执行完后,对结果集进行聚合计算,可以理解成所有逻辑都执行完,再进行count函数的执行。即将原表分组,分组后有4个人,count(*) over()(范围是整个结果集)对这4个人分别统计。对Jack,count(*)是4。对Mike,count(*)也是4。

结果1			|		结果2
Jack    5		|		Jack    4
Mike    2		|		Mike    4
Rose    4		|		Rose    4
Tony    3		|		Tony    4

over()

    计算所有客户购买金额的总额,窗口大小是整个结果集。

select *,sum(cost) over() summary from business;
name    orderdate       cost   summary
Rose    2017-04-13      94      661
Mike    2017-06-12      80      661
Rose    2017-04-11      75      661
Mike    2017-05-10      12      661
Rose    2017-04-09      68      661
Rose    2017-04-08      62      661
Jack    2017-01-08      55      661
Tony    2017-01-07      50      661
Jack    2017-04-06      42      661
Jack    2017-01-05      46      661
Tony    2017-01-04      29      661
Jack    2017-02-03      23      661
Tony    2017-01-02      15      661
Jack    2017-01-01      10      661

over(distribute by)

    计算每个客户的购买金额总额,窗口大小是分区的。
    可以使用over(partition by)
    distribute by 或 partition by子句创建的分区是独立于结果集的,创建的分区是进行聚合运算的,而不同的行开窗函数所创建的分区互不干扰。如果需要对结果分组,则需要在外层添加group by。

select *,sum(cost) over(distribute by name) summary from business;
name    orderdate       cost   summary
Jack    2017-01-01      10      176
Jack    2017-04-06      42      176
Jack    2017-01-05      46      176
Jack    2017-02-03      23      176
Jack    2017-01-08      55      176
Mike    2017-06-12      80      92
Mike    2017-05-10      12      92
Rose    2017-04-13      94      299
Rose    2017-04-11      75      299
Rose    2017-04-09      68      299
Rose    2017-04-08      62      299
Tony    2017-01-04      29      94
Tony    2017-01-02      15      94
Tony    2017-01-07      50      94

over(sort by)

    按日期计算购买金额的累加值,窗口大小随着行的变化而变化。
    可以使用over(order by)

select *,sum(cost) over(sort by orderdate) summary from business;
name    orderdate       cost   summary
Jack    2017-01-01      10      10
Tony    2017-01-02      15      25
Tony    2017-01-04      29      54
Jack    2017-01-05      46      100
Tony    2017-01-07      50      150
Jack    2017-01-08      55      205
Jack    2017-02-03      23      228
Jack    2017-04-06      42      270
Rose    2017-04-08      62      332
Rose    2017-04-09      68      400
Rose    2017-04-11      75      475
Rose    2017-04-13      94      569
Mike    2017-05-10      12      581
Mike    2017-06-12      80      661

    按日期计算购买金额的平均值

select *,avg(cost) over(sort by orderdate) average from business;
name    orderdate       cost    average
Jack    2017-01-01      10      10.0
Tony    2017-01-02      15      12.5
Tony    2017-01-04      29      18.0
Jack    2017-01-05      46      25.0
Tony    2017-01-07      50      30.0
Jack    2017-01-08      55      34.166666666666664
Jack    2017-02-03      23      32.57142857142857
Jack    2017-04-06      42      33.75
Rose    2017-04-08      62      36.888888888888886
Rose    2017-04-09      68      40.0
Rose    2017-04-11      75      43.18181818181818
Rose    2017-04-13      94      47.416666666666664
Mike    2017-05-10      12      44.69230769230769
Mike    2017-06-12      80      47.214285714285715

    第一个是10÷1,第二个是(10+15)÷2,第三个是(10+15+29)÷3,以此类推

over(distribute by sort by)

    按日期计算每个客户购买金额的累加值,窗口大小随着行的变化而变化。
    可以使用over(partition by order by)

select *,sum(cost) over(distribute by name sort by orderdate) summary from business;
name    orderdate       cost   summary
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
Mike    2017-05-10      12      12
Mike    2017-06-12      80      92
Rose    2017-04-08      62      62
Rose    2017-04-09      68      130
Rose    2017-04-11      75      205
Rose    2017-04-13      94      299
Tony    2017-01-02      15      15
Tony    2017-01-04      29      44
Tony    2017-01-07      50      94

over()的其它参数

    CURRENT ROW:当前行
    n PRECEDING:往前 n 行数据
    n FOLLOWING:往后 n 行数据
    UNBOUNDED PRECEDING:表示从前面的起点
    UNBOUNDED FOLLOWING:表示到后面的终点
    可以任意组合:

over(rows between UNBOUNDED PRECEDING and current row)
over(distribute by name rows between 1 PRECEDING and 1 FOLLOWING) #范围为分区内,前一行到后一行
over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING )

LAG(col,n)和LEAD(col,n)

    LAG(col,n):往前第 n 行数据;
    LEAD(col,n):往后第 n 行数据;
    这两个函数可以有第三个参数,当第一个参数为NULL时,可以用第三个代替,如 lag(cost,1,0)。
    需求:获取客户当前购买时间与上一次购买相差的天数。

name    orderdate       cost    last_date       time_interval
Jack    2017-01-01      10      NULL  		    NULL
Jack    2017-01-05      46      2017-01-01      4
Jack    2017-01-08      55      2017-01-05      3
Jack    2017-02-03      23      2017-01-08      26
Jack    2017-04-06      42      2017-02-03      62
Mike    2017-05-10      12      NULL   		    NULL
Mike    2017-06-12      80      2017-05-10      33
Rose    2017-04-08      62      NULL   			NULL
Rose    2017-04-09      68      2017-04-08      1
Rose    2017-04-11      75      2017-04-09      2
Rose    2017-04-13      94      2017-04-11      2
Tony    2017-01-02      15      NULL   			NULL
Tony    2017-01-04      29      2017-01-02      2
Tony    2017-01-07      50      2017-01-04      3
select
	name,orderdate,cost,last_date,
	if(last_date=NULL,NULL,datediff(orderdate,last_date)) time_interval
from (
select
	name,orderdate,cost,
	lag(orderdate,1) over(distribute by name sort by orderdate) last_date
from
	business) t1;

NTILE (n)

    将有序分区中的行分发到指定数据的组中,即划分为n等份,每个组有编号,编号从1开始。NTILE 不支持row between。

select name,orderdate,cost,ntile(4) over() groupnum from business;
name    orderdate       cost    groupnum
Rose    2017-04-13      94      1
Mike    2017-06-12      80      1
Rose    2017-04-11      75      1
Mike    2017-05-10      12      1
Rose    2017-04-09      68      2
Rose    2017-04-08      62      2
Jack    2017-01-08      55      2
Tony    2017-01-07      50      2
Jack    2017-04-06      42      3
Jack    2017-01-05      46      3
Tony    2017-01-04      29      3
Jack    2017-02-03      23      4
Tony    2017-01-02      15      4
Jack    2017-01-01      10      4

    需求:查询前20%时间的购买明细

select
	name,orderdate,cost,groupnum
from (
select
	name,orderdate,cost,
	ntile(5) over(order by orderdate) groupnum
from
	business) t1
where
	groupnum=1;
name    orderdate       cost    groupnum
Jack    2017-01-01      10      1
Tony    2017-01-02      15      1
Tony    2017-01-04      29      1

first_value和last_value

    first_value(column):取(分组内排序后)截止到当前行的第一个值
    last_value(column):取(分组内排序后)截止到当前行最后一个值

select
	name,orderdate,cost,
	first_value(orderdate) over(partition by name order by orderdate) as date1,
	last_value(orderdate) over(partition by name order by orderdate) as date2
from business;
name    orderdate       cost    date1   		date2
Jack    2017-01-01      10      2017-01-01      2017-01-01
Jack    2017-01-05      46      2017-01-01      2017-01-05
Jack    2017-01-08      55      2017-01-01      2017-01-08
Jack    2017-02-03      23      2017-01-01      2017-02-03
Jack    2017-04-06      42      2017-01-01      2017-04-06
Mike    2017-05-10      12      2017-05-10      2017-05-10
Mike    2017-06-12      80      2017-05-10      2017-06-12
Rose    2017-04-08      62      2017-04-08      2017-04-08
Rose    2017-04-09      68      2017-04-08      2017-04-09
Rose    2017-04-11      75      2017-04-08      2017-04-11
Rose    2017-04-13      94      2017-04-08      2017-04-13
Tony    2017-01-02      15      2017-01-02      2017-01-02
Tony    2017-01-04      29      2017-01-02      2017-01-04
Tony    2017-01-07      50      2017-01-02      2017-01-07

rank(),dense_rank(),row_number()

    rank()跳跃排序,排序相同时会重复,之后跳过若干个次序
    dense_rank():排序相同时会重复,之后紧跟着上一个次序,序号是连续的
    row_number():根据顺序计算
    注意:使用排序函数的时候,NULL值是最大的,如果排序字段有NULL, 可能造成NULL字段排在最前面,影响排序结果。所以可以这样:rank() over(partition by name order by score desc nulls last)

select
	name,score,
	rank() over(order by score desc) rank,
	dense_rank() over(order by score desc) dense_rank,
	row_number() over(order by score desc) row_number 
from score;
name   score  rank  dense_rank      row_number
D      96     1       	1       		1
G      90     2       	2       		2
E      90     2       	2       		3
C      90     2       	2       		4
A      90     2       	2       		5
H      85     6       	3       		6
F      77     7       	4       		7
B      68     8       	5       		8

    注意:使用排序函数必须在over()子句里面选择对某一列排序,才能生成正确的序号,因为外层的order by是最后执行的。看下面这个例子:

select name,score,row_number() over() row_number from score order by score desc;
name    score   row_number
D       96      5
A       90      8
C       90      6
E       90      4
G       90      2
H       85      1
F       77      3
B       68      7

    从上面这个例子可以看到,order by子句是在窗口函数之后执行的

select name,score,row_number() over(order by score desc) row_number from score;
name    score   row_number
D       96      1
G       90      2
E       90      3
C       90      4
A       90      5
H       85      6
F       77      7
B       68      8

  • show functions:查看系统自带的函数
  • desc function function_name:显示自带的函数的用法
  • desc function extended function_name:显示自带的函数的用法
        

猜你喜欢

转载自blog.csdn.net/H_X_P_/article/details/106452854
今日推荐