在任何一种编程语言中,函数可以说是必不可少的,像mysql、oracle中,提供了很多内置函数,或者通过自定义函数的方式进行定制化使用,而hive作为一门数据分析软件,随着版本的不断更新迭代,也陆续出现了很多函数,以满足日常数据查询与分析的各种场景。
Hive内建了不少函数,用于满足用户不同使用需求,提高SQL编写效率,查看hive的函数,可以通过下面的方式:
1、show functions —— 查看当下可用的所有函数;
2、describe function extended funcname —— 查看函数的具体使用方式;
show functions 查看当前版本下hive函数

这以上随机截取了一部分,更详细的可以参考官方手册:hive官方文档,里面有hive函数的详细资料;

Hive函数分为两大类:
包括:数值类型函数、日期类型函数、字符串类型函数、集合函数、条件函数等
根据输入输出的行数可分为3类:UDF、UDAF、UDTF
用户自定义函数根据函数输入输出的行数进行划分,具体来说如下:
UDF(User-Defined-Function)
普通函数,一进一出;

UDAF(User-Defined Aggregation Function)
聚合函数,多进一出;

UDTF(User-Defined Table-Generating Functions)
表生成函数,一进多出;

UDF分类标准本来针对的是用户自己编写开发实现的函数,UDF分类标准可以扩大到Hive的所有函数中:包括内置函数和用户自定义函数;
因为不管是什么类型的函数,一定满足于输入输出的要求,那么从输入几行和输出几行上来划分没有任何问题。 千万不要被UD(User-Defined)这两个字母所迷惑,照成视野的狭隘。
内置函数(build-in)顾名思义,指的是Hive开发实现好,直接可以使用的函数,也叫做内建函数,官方文档地址:hive官方文档,内置函数根据应用归类整体可以分为8大种类型,我们将对其中重要的,使用频率高的函数使用进行详细的说明;
8大类型的内置函数
字符串函数可以说在日常工作中使用的很频繁的了,下面列举一些常用的字符串函数
| 字符串长度函数:length |
| 字符串反转函数:reverse |
| 字符串连接函数:concat |
| 带分隔符字符串连接函数:concat_ws |
| 字符串截取函数:substr,substring |
| 字符串转大写函数:upper,ucase |
| 字符串转小写函数:lower,lcase |
| 去空格函数:trim |
| 左边去空格函数:ltrim |
| 右边去空格函数:rtrim |
| 正则表达式替换函数:regexp_replace |
| 正则表达式解析函数:regexp_extract |
| URL解析函数:parse_url |
| json解析函数:get_json_object |
| 空格字符串函数:space |
| 重复字符串函数:repeat |
| 首字符ascii函数:ascii |
| 左补足函数:lpad |
| 右补足函数:rpad |
| 分割字符串函数: split |
| 集合查找函数: find_in_set |
用于拼接字符串,或者将不同类型的字符串拼接在一起;
select concat("angela","baby");
带分隔符字符串连接函数:concat_ws(separator, [string | array(string)]+)
select concat_ws('.', 'www', array('taobao', 'com'));

substr(str, pos[, len]) 或者 substring(str, pos[, len])
select substr("angelababy",-2); --pos是从1开始的索引,如果为负数则倒着数
select substr("angelababy",2,2);
regexp_replace(str, regexp, rep)
select regexp_replace('100-200', '(\\d+)', 'num');

regexp_extract(str, regexp[, idx]) ,提取正则匹配到的指定组内容
select regexp_extract('100-200', '(\\d+)-(\\d+)', 2);

parse_url ,注意要想一次解析出多个 可以使用parse_url_tuple这个UDTF函数
select parse_url('http://www.taobao.com/path/p1.action?query=1', 'HOST');

split(str, regex)
select split('apache hive', '\\s+');

get_json_object(json_txt, path),其中:json_txt表示json对象;
select get_json_object('[{"website":"www.taobao.com","name":"allenwoon"}, {"website":"cloud.taobao.com","name":"carbondata 中文文档"}]', '$.[1].website');

length(str | binary)

select reverse("angelababy");
--字符串连接函数:concat(str1, str2, ... strN)
--字符串转大写函数:upper,ucase
select upper("angelababy");
select ucase("angelababy");
--字符串转小写函数:lower,lcase
select lower("ANGELABABY");
select lcase("ANGELABABY");
--去空格函数:trim 去除左右两边的空格
select trim(" angelababy ");
--左边去空格函数:ltrim
select ltrim(" angelababy ");
--右边去空格函数:rtrim
select rtrim(" angelababy ");
--空格字符串函数:space(n) 返回指定个数空格
select space(4);
--重复字符串函数:repeat(str, n) 重复str字符串n次
select repeat("angela",2);
--首字符ascii函数:ascii
select ascii("angela"); --a对应ASCII 97
--左补足函数:lpad
select lpad('hi', 5, '??'); --???hi
select lpad('hi', 1, '??'); --h
--右补足函数:rpad
select rpad('hi', 5, '??');
--集合查找函数: find_in_set(str,str_array)
select find_in_set('a','abc,b,ab,c,def');
日期函数也是一种使用非常频繁的函数,有必要深入的掌握,以下列举常用的日期函数
| 获取当前日期: current_date |
| 获取当前时间戳: current_timestamp |
| UNIX时间戳转日期函数: from_unixtime |
| 获取当前UNIX时间戳函数: unix_timestamp |
| 日期转UNIX时间戳函数: unix_timestamp |
| 指定格式日期转UNIX时间戳函数: unix_timestamp |
| 抽取日期函数: to_date |
| 日期转年函数: year |
| 日期转月函数: month |
| 日期转天函数: day |
| 日期转小时函数: hour |
| 日期转分钟函数: minute |
| 日期转秒函数: second |
| 日期转周函数: weekofyear |
| 日期比较函数: datediff |
| 期增加函数: date_add |
| 日期减少函数: date_sub |
select current_date();

current_timestamp,同一查询中对current_timestamp的所有调用均返回相同的值
select current_timestamp();

select unix_timestamp();

datediff ,日期格式要求为:'yyyy-MM-dd HH:mm:ss' or 'yyyy-MM-dd'
select datediff('2012-12-08','2012-05-09');

--日期转UNIX时间戳函数: unix_timestamp
select unix_timestamp("2011-12-07 13:01:03");--指定格式日期转UNIX时间戳函数: unix_timestamp
select unix_timestamp('20111207 13:01:03','yyyyMMdd HH:mm:ss');--UNIX时间戳转日期函数: from_unixtime
select from_unixtime(1618238391);
select from_unixtime(0, 'yyyy-MM-dd HH:mm:ss');--日期增加函数: date_add
select date_add('2012-02-28',10);--日期减少函数: date_sub
select date_sub('2012-01-1',10);--抽取日期函数: to_date
select to_date('2009-07-30 04:17:52');--日期转年函数: year
select year('2009-07-30 04:17:52');--日期转月函数: month
select month('2009-07-30 04:17:52');--日期转天函数: day
select day('2009-07-30 04:17:52');--日期转小时函数: hour
select hour('2009-07-30 04:17:52');--日期转分钟函数: minute
select minute('2009-07-30 04:17:52');--日期转秒函数: second
select second('2009-07-30 04:17:52');--日期转周函数: weekofyear 返回指定日期所示年份第几周
select weekofyear('2009-07-30 04:17:52');
当需要对数据进行一些特殊的运算,比如取整,随机数等,就需要用到hive中的熟悉函数,以下列举常用的一些数学函数;
| 取整函数: round |
| 指定精度取整函数: round |
| 向下取整函数: floor |
| 向上取整函数: ceil |
| 取随机数函数: rand |
| 二进制函数: bin |
| 进制转换函数: conv |
| 绝对值函数: abs |
round 返回double类型的整数值部分 (遵循四舍五入)

round(double a, int d) ,返回指定精度d的double类型
select round(3.1415926,4);

rand ,每次执行都不一样 返回一个0到1范围内的随机数
select rand();
--向下取整函数: floor
select floor(3.1415926);
select floor(-3.1415926);
--向上取整函数: ceil
select ceil(3.1415926);
select ceil(-3.1415926);--指定种子取随机数函数: rand(int seed) 得到一个稳定的随机数序列
select rand(3);--二进制函数: bin(BIGINT a)
select bin(18);
--进制转换函数: conv(BIGINT num, int from_base, int to_base)
select conv(17,10,16);
--绝对值函数: abs
select abs(-3.9);
size(Map
select size(`array`(11,22,33));
select size(`map`("id",10086,"name","zhangsan","age",18));

map_keys(Map
select map_keys(`map`("id",10086,"name","zhangsan","age",18));

map_values(Map
select map_values(`map`("id",10086,"name","zhangsan","age",18));

array_contains(Array
select array_contains(`array`(11,22,33),11);
select array_contains(`array`(11,22,33),66);

sort_array(Array
select sort_array(`array`(12,2,32));

if(boolean testCondition, T valueTrue, T valueFalseOrNull) ,有没有觉得这个语法和 mybatis 中的动态sql有点类似呢?
select if(1=2,100,200);
select if(sex ='男','M','W') from student limit 3;

isnull( a )
select isnull("allen");
select isnull(null);

isnotnull ( a )
select isnotnull("allen");
select isnotnull(null);
nvl(T value, T default_value)
select nvl("allen","baidu");
select nvl(null,"baidu");
COALESCE(T v1, T v2, ...) ,返回参数中的第一个非空值;如果所有值都为NULL,那么返回NULL;
select COALESCE(null,11,22,33);
select COALESCE(null,null,null,33);
select COALESCE(null,null,null);

CASE a WHEN b THEN c [WHEN d THEN e]* [ELSE f] END
select case 100 when 50 then 'tom' when 100 then 'mary' else 'tim' end;
select case sex when '男' then 'male' else 'female' end from student limit 3;
--nullif( a, b ):
-- 如果a = b,则返回NULL,否则返回一个
select nullif(11,11);
select nullif(11,12);--assert_true(condition)
--如果'condition'不为真,则引发异常,否则返回null
SELECT assert_true(11 >= 0);
SELECT assert_true(-1 >= 0);
任意数据类型之间转换:cast
select cast(12.14 as bigint);
select cast(12.14 as string);
select cast("hello" as int);
将查询回的数据,大写字母转换为X,小写字母转换为x,数字转换为n
select mask("abc123DEF");
select mask("abc123DEF",'-','.','^'); --自定义替换的字母

对前n个进行脱敏替换

对后n个进行脱敏替换
select mask_last_n("abc123DEF",4);

除了前n个字符,其余进行掩码处理
select mask_show_first_n("abc123DEF",4);
除了后n个字符,其余进行掩码处理
select mask_show_last_n("abc123DEF",4);
返回字符串的hash编码
select mask_hash("abc123DEF");
如果你要调用的java方法所在的jar包,而不是hive自带的 ,可以使用add jar 的方式添加进来,hive调用java方法格式为:java_method(class, method[, arg1[, arg2..]]);
select java_method("java.lang.Math","max",11,22);

用户自定义函数简称UDF,源自于英文user-defined function,在系统内置的函数无法满足实际的业务场景需求时,可以考虑使用自定义函数 ,关于自定义函数的概念在本文开头有过介绍;
UDF 普通函数
特点是一进一出,也就是输入一行输出一行,比如round这样的取整函数,接收一行数据,输出的还是一行数据;

UDAF 聚合函数
UDAF 聚合函数,A所代表的单词就是Aggregation聚合的意思,多进一出,也就是输入多行输出一行;
如下图

UDTF 表生成函数
这类型的函数作用返回的结果类似于表,同时,UDTF函数也是我们接触比较少的函数,比如explode函数;
如下图所示

使用explode函数就可以得到一进多出的效果;

explode 函数属于UDTF类型函数,explode接收map、array类型的数据作为输入,然后把输入数据中的每个元素拆开变成一行数据,一个元素一行,explode执行效果正好满足于输入一行输出多行,所有叫做UDTF函数;
1、一般情况下,explode函数可以直接单独使用即可;
2、也可以根据业务需要结合lateral view侧视图一起使用;
使用示例
explode(array) ,将array里的每个元素生成一行;
explode(map) ,将map里的每一对元素作为一行,其中key为一列,value为一列;

有如下的数据

现在需要把这份数据最终转换成下面的表的数据

create table the_nba_championship(team_name string,champion_year array
) row format delimited
fields terminated by ','
collection items terminated by '|';
执行上面的sql建表

load data local inpath '/usr/local/soft/hivedata/The_NBA_Championship.txt' into table the_nba_championship;

检查数据

俗称炸开
select explode(champion_year) from the_nba_championship;
执行结果,可以看到就把年份分成了一行行数据;

select a.team_name,b.year
from the_nba_championship a lateral view explode(champion_year) b as year
order by b.year desc;
执行结果

可能有同学想,为什么不使用下面的sql呢?
select team_name,explode(champion_year) from the_nba_championship;
其实这个是错误的,执行一下就会报错,这就要解释下UDTF的语法限制了;
Lateral View 侧视图概述
Lateral View是一种特殊的语法,主要搭配UDTF类型函数一起使用,用于解决UDTF函数的一些查询限制的问题,一般只要使用UDTF,就会固定搭配lateral view使用;
官方链接地址:hive侧视图地址
Lateral View 原理
可以对照下图进行理解;

select …… from tabelA lateral view UDTF(xxx) 别名 as col1,col2,col3……;
获取球队的名称,以及荣获冠军的年份
select a.team_name ,b.year
from the_nba_championship a lateral view explode(champion_year) b as year;
执行结果

select a.team_name ,b.year
from the_nba_championship a lateral view explode(champion_year) b as year
order by b.year desc;
执行结果

select a.team_name ,count(*) as nums
from the_nba_championship a lateral view explode(champion_year) b as year
group by a.team_name
order by nums desc;
执行结果

在很多业务场景下,需要对表的数据进行汇总(聚合),得到一个聚合的结果或者分组聚合的结果进行报表展示等,比如我们熟悉的mysql,mongodb,es等都提供了丰富的聚合函数对数据进行聚合统计分析。
hive 聚合函数的功能是:对一组值执行计算并返回单一的值
1、聚合函数是典型的输入多行输出一行,使用Hive的分类标准,属于UDAF类型函数;
2、通常搭配Group By语法一起使用,分组后进行聚合操作;
drop table if exists student;
create table student(num int,name string,sex string,age int,dept string)
row format delimited
fields terminated by ',';
执行sql建表


--count(*):所有行进行统计,包括NULL行
--count(1):所有行进行统计,包括NULL行
--count(column):对column中非Null进行统计
执行下面的sql
select count(*) as cnt1,count(1) as cnt2 from student;
执行结果

这个同mysql的操作基本一致,这里注意group by语法限制
select sex,count(*) as cnt from student group by sex;
执行结果

select count(*) as cnt1,avg(age) as cnt2 from student;
执行结果

与case when条件转换函数、coalesce函数、if函数使用
selectsum(CASE WHEN sex = '男'THEN 1 ELSE 0 END)
from student;selectsum(if(sex = '男',1,0))
from student;
执行结果


CREATE TABLE tmp_1 (val1 int, val2 int);
INSERT INTO TABLE tmp_1 VALUES (1, 2),(null,2),(2,3);
select * from tmp_1;
依次执行上面的sql观察结果

在存在null数据的情况下,进行下面的聚合看看效果如何
select sum(val1), sum(val1 + val2) from tmp_1;
从执行结果来看,第二行数据(NULL, 2) 在进行sum(val1 + val2)的时候会被忽略;

这个可以使用coalesce函数解决
selectsum(coalesce(val1,0)),sum(coalesce(val1,0) + val2)
from tmp_1;
这时执行上面的sql就能得到期望的结果了

此场景下,会编译期间会自动设置只启动一个reduce task处理数据 可能造成数据拥堵,如下的聚合:
select count(distinct sex) as cnt1 from student;
在这种情况下,可以考了先去重 在聚合 通过子查询完成,因为先执行distinct的时候 可以使用多个reducetask来跑数据,改进后的sql如下:
select count(*) as gender_uni_cnt
from (select distinct sex from student) a;
这里使用了struct来构造数据 然后针对struct应用max找出最大元素 然后取值 select sex
select sex,
max(struct(age, name)).col1 as age,
max(struct(age, name)).col2 as name
from student
group by sex;
执行上面的sql观察结果

常用的增强聚合函数包括:grouping_sets、cube、rollup这几个函数,它们主要适用于OLAP多维数据分析模式中,多维分析中的维指的分析问题时看待问题的维度、角度,接下来通过操作演示下增强聚合的使用。
建表并加载数据
CREATE TABLE cookie_info(month STRING,day STRING,cookieid STRING
) ROW FORMAT DELIMITED
FIELDS TERMINATED BY ',';load data local inpath '/usr/local/soft/selectdata/cookie_info.txt' into table cookie_info;
查询数据是否加载成功

-- grouping_id —— 表示这一组结果属于哪个分组集合;
-- 根据grouping sets中的分组条件month,day,1是代表month,2是代表day;
SELECTmonth,day,COUNT(DISTINCT cookieid) AS nums,GROUPING__ID
FROM cookie_info
GROUP BY month,day
GROUPING SETS (month,day) --这里是关键
ORDER BY GROUPING__ID;
执行结果

从这个执行结果来看,其实group sets的操作相当于是把两种聚合结果做了union操作,等价于下面的sql;
SELECT month,NULL,COUNT(DISTINCT cookieid) AS nums,1 AS GROUPING__ID FROM cookie_info GROUP BY month
UNION ALL
SELECT NULL as month,day,COUNT(DISTINCT cookieid) AS nums,2 AS GROUPING__ID FROM cookie_info GROUP BY day;
从上一个操作对照理解这个sql就很好理解了
SELECTmonth,day,COUNT(DISTINCT cookieid) AS nums,GROUPING__ID
FROM cookie_info
GROUP BY month,day
GROUPING SETS (month,day,(month,day)) --1 month 2 day 3 (month,day)
ORDER BY GROUPING__ID;
执行sql观察结果

总结来说:grouping sets是一种将多个group by逻辑写在一个sql语句中的便利写法。等价于将不同维度的GROUP BY结果集进行UNION ALL。GROUPING__ID表示结果属于哪一个分组集合。
cube表示根据GROUP BY的维度的所有组合进行聚合。对于cube来说,如果有n个维度,则所有组合的总个数是:2^n;
比如cube有a,b,c 3个维度,则所有组合情况是: (a,b,c),(a,b),(b,c),(a,c),(a),(b),(c),()
cube 的语法功能指的是:根据GROUP BY的维度的所有组合进行聚合,rollup是cube的子集,以最左侧的维度为主,从该维度进行层级聚合;
比如ROLLUP有a,b,c3个维度,则所有组合情况是:(a,b,c),(a,b),(a),()
按照上面的理论说明,使用cube进行聚合下面的sql;
SELECTmonth,day,COUNT(DISTINCT cookieid) AS nums,GROUPING__ID
FROM cookie_info
GROUP BY month,day
WITH CUBE
ORDER BY GROUPING__ID;
执行sql,观察执行结果;

从结果来看,得到了多个维度的聚合统计结果,其实就等价于下面的sql;
SELECT NULL,NULL,COUNT(DISTINCT cookieid) AS nums,0 AS GROUPING__ID FROM cookie_info
UNION ALL
SELECT month,NULL,COUNT(DISTINCT cookieid) AS nums,1 AS GROUPING__ID FROM cookie_info GROUP BY month
UNION ALL
SELECT NULL,day,COUNT(DISTINCT cookieid) AS nums,2 AS GROUPING__ID FROM cookie_info GROUP BY day
UNION ALL
SELECT month,day,COUNT(DISTINCT cookieid) AS nums,3 AS GROUPING__ID FROM cookie_info GROUP BY month,day;
以month维度进行层级聚合;
SELECTmonth,day,COUNT(DISTINCT cookieid) AS nums,GROUPING__ID
FROM cookie_info
GROUP BY month,day
WITH ROLLUP
ORDER BY GROUPING__ID;
执行结果;

再把month和day调换顺序,则以day维度进行层级聚合;
SELECTday,month,COUNT(DISTINCT cookieid) AS uv,GROUPING__ID
FROM cookie_info
GROUP BY day,month
WITH ROLLUP
ORDER BY GROUPING__ID;
执行结果

本篇通过大量的篇幅展示了hive中各类函数的详细使用,希望对看到的小伙们有用,不足之处,敬请执教,本篇到此结束,谢谢观看!
上一篇:DaVinci:调色版本
下一篇:动态规划之01背包问题