目录
空字段赋值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
字符串类
- 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)
功能:将一列中复杂的array或map类型才分成多行(一行转多行)。
用法: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:显示自带的函数的用法