有时候我们查到的一些值是NULL
, 但是有时候NULL不能准确的表达含义和参与运算(NULL与任何值运算都为NULL), 比如说工资的奖金
这时我们可以使用nvl()
函数, 在查询时将NULL值转换为一个默认值
nvl(<可能为空的字段>, <自定义默认值>);
举例 查询员工的工资和奖金, 如果为NULL, 则显示0
select 工资, nvl(奖金, 0)
from 员工表;
这时, 如果某个人的奖金为NULL, 就会显示为0而不是NULL
有点类似于MySQL中的if()
函数
case <字段名> when <如果等于这个值> then <显示的值> else <如果不等于显示的值> end
举例 在员工表中, 统计每个部门男女各有多少人
select
部门名称,
sum(case 性别 when '男' then 1 else 0 end) 男,
sum(case 性别 when '女' then 1 else 0 end) 女
from 员工表
group by 部门名称;
说明
这里巧妙的运用了
case when
, 用男性举例, 如果是男性, 就显示1, 然后再把1 作为sun()
的参数, 就实现了累加的效果
它的主要作用是将某字段的值进行去重汇总,产生一个array类型字段, 一般用于分组的时候
collect(<字段名>)
注意
函数只接受基本数据类型, 不接受集合类型
举例 加入一张表里有学生姓名(string)
和班级(string)
这两个字段, 要求查询每个班级的名称和这个班里的所有同学的姓名
select 班级, collect_set(学生姓名姓名) as 姓名
from 学生表
group by 班级;
结果举例, 结果会是以下形式:
班级 | 姓名 |
---|---|
1 | [“小明”,“小刚”] |
2 | [“红红”, “莉莉”, “冬梅”] |
与上面相反, Hive还可以将一个集合类型拆分成多行
explode()
是一个UDTF函数, 即user-defined table generating functions, 说白了就是一个可以生成一个新表的函数
explode(<要拆分的字段名>)
举例 假设有下面一张学生爱好表
姓名 | 爱好 |
---|---|
小明 | 唱, 跳, rap |
小刚 | 篮球 |
select explode(爱好) as 爱好
from 学生爱好;
结果:
爱好 |
---|
唱 |
跳 |
rap |
篮球 |
注意
虽然可以将集合(列)转换为行, 但是只是使用
expode()
函数,的话, 也只能查询分割的这一列的内容, 从上面这张表来说, 只能查询爱好
这一字段如果想下面这样查询, 就会出错:select explode(爱好) as 爱好, 姓名 from 学生爱好;
因为UDTF函数不能和普通字段一起查询, 这时我们就需要一个可以与原表绑定的方法, 也就是
lateral view
表面含义是"侧视图", 是一种视图, 他的作用就是可以将UDTF分割后生成的新表, 和原来的行相关联, 从而可以是普通字段和UDTF字段一起查询, 其实就是将UDTF生成的表看做一个视图, 然后与原表的行相关联
使用lateral view 需要指定生成的视图名称
select <视图名>.*
from <原表名>
lateral view <UDTF函数> <视图名>
举例 跟上面那个例子一样, 拆分爱好
字段
select 爱好视图.*
from 学生爱好表
lateral view explode(爱好) 爱好视图;
结果跟上面是一样的, 因为只是将explode()生成的表看做一个视图, 所以这样单独使用lateral view
并没有什么意义
我们是想要将该视图和原表的其他字段一起查询, 需要将该视图看做一个列, 还要指定这个列别名
select <列表名>, <原表中的字段>
from <原表名>
lateral view <视图名> as <列别名>
举例 根据上面的例子, 将每个爱笑对应的学生显示出来
select 爱好, 姓名
from 学生爱好表
lateral view explode(爱好) 爱好视图 as 爱好
这样就出现我们想要的结果了:
爱好 | 姓名 |
---|---|
唱 | 小明 |
跳 | 小明 |
rap | 小明 |
篮球 | 小刚 |
开窗函数有点类似于group by
分组函数, 但是分组查询时, 每个组只能返回一个聚合值, 如果说我们想要返回多个值的时候, 就没法使用分组查询完成了, 比如, 在电商网站中, 我想要查询每个每个顾客上一次购买物品的时间, 或者在流量分析中, 查询每个用户本周每天使用流量的累积
开窗函数一般分为两种:
开窗函数在查询结果中其实就相当于一列, 该列使用over()
修饰:
select <字段名称 | 聚合函数> over(<窗口范围条件>) [as <别名>]
from ...
说明
窗口范围条件:
参数 含义 默认(不填) 整个查询结果 partition by <字段名> 根据分区开窗, 每个分区一个窗口 order by <字段名> 跟在 partition by <字段名>
后面, 给当前窗口排序distribute by <字段名> sort by <字段名> 分区排序(在多个Reducer的情况下, 对每个Reducer结果内部排序) rows 指定当前窗口包括那一行 rows属性
属性 含义 CURRENT ROW 当前行 n PRECEDING 往前n行 n FOLLOWING 往后n行数据 UNBOUNDED PRECEDING 表示从前面的起点 UNBOUNDED FOLLOWING 表示到后面的终点 LAG(col,n,default_val) 往前第n行数据, 如果数据为null, 就显示第三个参数 LEAD(col,n, default_val) 往后第n行数据, 如果数据为null, 就显示第三个参数 NTILE(n)
下面我们模拟一个名为购物表
电商网站的表(这里方便查看使用中文作为字段名, 但是在实际中不要使用中文):
行 | 类型 | 含义 |
---|---|---|
姓名 | string | 姓名 |
购买日期 | string | 该用户购买物品的日期 |
消费 | double | 该用户本次购买物品的花费 |
举例 1 查询2020年1月购买过的顾客以及该月份购买的总人数
select 姓名, count(*) over()
from 购物表
where substring(购买日期, 1, 7) = '2020-01'
group by 姓名;
举例 2 将每个顾客的消费值, 按照日期累加显示
select 姓名, sum(消费) over(partition by 姓名 order by 购买日期)
from 购物表 ;
举例 3 同例2, 这里查询的是每个顾客本次加上上一次的消费总额
其实这里就是给每行结果开一个窗口, 窗口中是 当前消费额+上一次的消费额
select
姓名,
购买日期,
sum(消费) over(partition by 姓名 order by 购买日期 rows between 1 preceding and current row)
from business ;
举例 4 查询每个顾客购买的信息详情和上一次购买的时间
其实这里就是给每行结果开一个窗口, 窗口中是 上一次的购物时间, 这里添加了一个默认值(“第一次购买”), 如果没有上一次, 说明是第一次购买, 即上一次为空, 这时就会显示"第一次购买"
select
姓名,
购买日期,
lag(购买日期, 1, "第一次购买") over(partition by 姓名 order by 购买日期)
from 购物表 ;
通过开窗函数可以查看当前行的序号(排名), 开窗函数中有特定的函数用户获取序号, 总共有3中排序方式:
排序相同时会重复,总数不会变
排序相同时会重复,总数会减少
会根据顺序计算
**举例 ** 这种排序在成绩单上很常见, 假设有一张成绩表, 对比几种排名
姓名(string) | 成绩(double) |
---|---|
小明 | 95 |
小刚 | 95 |
小红 | 80 |
小王 | 75 |
老王 | 75 |
select
姓名,
成绩,
rank() over(order by 成绩) as rank排名,
dense_rank() over(order by 成绩) as dense_rank排名,
row_number() over(order by 成绩) as row_number排名
from 成绩表;
结果:
姓名 | 成绩 | rank排名 | dense_rank排名 | row_number排名 |
---|---|---|---|---|
小明 | 95 | 1 | 1 | 1 |
小刚 | 95 | 1 | 1 | 2 |
小红 | 80 | 3 | 2 | 3 |
小王 | 75 | 4 | 3 | 4 |
老王 | 75 | 4 | 3 | 5 |
有时候Hive自带的函数不能满足我们的需求, 这时我们就需要自定义函数
UDF
(org.apache.hadoop.hive.ql.exec.UDF)evaluate()
的方法, 然后再里面实现自定义的函数体/lib/
目录下add jar "jar包路径";
create temporary function testUpper as "<包路径.类名>";
举例 自定义一个函数, 要求实现字符串全部转换为大写
<dependency>
<groupId>org.apache.hivegroupId>
<artifactId>hive-execartifactId>
<version>1.2.2version>
dependency>
UDF
父类, 自定义evaluate()
函数, 注意, 这里的evaluate()
不是UDF父类自带的函数, 需要自己命名成evaluatepackage udf;
import org.apache.hadoop.hive.ql.exec.UDF;
/**
* Description: 注意, 这里的报名最好是全英文路径,
*/
public class Upper extends UDF {
public String evaluate(String str) {
//判断是否为空, 如果为空, 则返回null
if (str == null) {
return null;
}
//不为空, 返回大写结果
return str.toUpperCase();
}
}
/lib/
目录下add jar /opt/software/hive/lib/testUpper.jar;
create temporary function testUpper as "udf.Upper";
select testUpper(stu_name)
from stu_info ;