大数据Hive--函数

一、函数

1.1 函数简介

Hive会将常用的逻辑封装成函数给用户进行使用,类似于Java中的函数。

好处:避免用户反复写逻辑,可以直接拿来使用。

Hive提供了大量的内置函数,按照其特点可大致分为如下几类:单行函数、聚合函数、炸裂函数、窗口函数。

  • 以下命令可用于查询所有内置函数的相关信息:

1)查看系统内置函数

show functions;

2)查看内置函数用法

desc function upper;

3)查看内置函数详细信息

desc function extended upper;

1.2 单行函数

单行函数的特点是一进一出,即输入一行,输出一行。

单行函数按照功能可分为如下几类: 日期函数、字符串函数、集合函数、数学函数、流程控制函数等。

1.2.1 算术运算函数

运算符描述
A+BA和B 相加
A-BA减去B
A*BA和B 相乘
A/BA除以B
A%BA对B取余
A&BA和B按位取与
A|BA和B按位取或
A^BA和B按位取异或
~AA按位取反

1.2.2 数值函数

1)round:四舍五入

select round(3.3);   3

2)ceil:向上取整

``select ceil(3.1) ;   4

3)floor:向下取整

select floor(4.8);  4

1.2.3 字符串函数

1)substring:截取字符串

语法一:substring(string A, int start) 
返回值:string 
说明:返回字符串A从start位置到结尾的字符串

语法二:substring(string A, int start, int len) 
返回值:string
说明:返回字符串A从start位置开始,长度为len的字符串

(1)获取第二个字符以后的所有字符

select substring("shanghai",2);
输出: hanghai

(2)从第3个字符开始,向后获取2个字符

select substring("shanghai",3,2);
输出: an

2)replace :替换

语法:replace(string A, string B, string C) 
返回值:string
说明:将字符串A中的子字符串B替换为C。

3)regexp_replace:正则替换

语法:regexp_replace(string A, string B, string C) 
返回值:string
说明:将字符串A中的符合java正则表达式B的部分替换为C。注意,在有些情况下要使用转义字符。
select regexp_replace('100-200', '(\\d+)', 'num') 
输出:num-num

4)regexp:正则匹配

语法:字符串 regexp 正则表达式
返回值:boolean
说明:若字符串符合正则表达式,则返回true,否则返回false。

(1)正则匹配成功,输出true

select 'dfsaaaa' regexp 'dfsa+'
输出:true

(2)正则匹配失败,输出false

select 'dfsaaaa' regexp 'dfsb+';
输出:false

5)repeat:重复字符串

语法:repeat(string A, int n)
返回值:string
说明:将字符串A重复n遍。

6)split :字符串切割

语法:split(string str, string pat) 
返回值:array
说明:按照正则表达式pat匹配到的内容分割str,分割后的字符串,以数组的形式返回。

7)nvl :替换null值

语法:nvl(A,B) 
说明:若A的值不为null,则返回A,否则返回B。 
select nvl(null,1); 
输出:1

8)concat :拼接字符串

语法:concat(string A, string B, string C, ……) 
返回:string
说明:将A,B,C……等字符拼接为一个字符串
select concat('beijing','-','shanghai','-','shenzhen');
输出:beijing-shanghai-shenzhen

9)concat_ws:以指定分隔符拼接字符串或者字符串数组

语法:concat_ws(string A, string…| array(string)) 
返回值:string
说明:使用分隔符A拼接多个字符串,或者一个数组的所有元素。
select concat_ws('-','beijing','shanghai','shenzhen');
输出:beijing-shanghai-shenzhen

10)get_json_object:解析json字符串

语法:get_json_object(string json_string, string path) 
返回值:string
说明:解析json的字符串json_string,返回path指定的内容。如果输入的json字符串无效,那么返回NULL。

(1)获取json数组里面的数据

select get_json_object('[{"name":"大海","sex":"男","age":"25"},{"name":"小张","sex":"男","age":"47"}]','$.[0]');
输出:{"name":"大海","sex":"男","age":"25"}

1.2.4 日期函数

1)unix_timestamp:返回当前或指定时间的时间戳

语法:unix_timestamp() 
返回值:bigint 
select unix_timestamp('2022/08/08 08-08-08','yyyy/MM/dd HH-mm-ss');  
输出:1659946088
-- 说明:-前面是日期后面是指,日期传进来的具体格式 

2)from_unixtime:转化UNIX时间戳(从 1970-01-01 00:00:00 UTC 到指定时间的秒数)到当前时区的时间格式

语法:from_unixtime(bigint unixtime[, string format]) 
返回值:string 
select from_unixtime(1659946088);   
输出:2022-08-08 08:08:08

3)current_date:当前日期

select current_date;     
输出:2024-01-01

4)current_timestamp:当前的日期加时间,并且精确的毫秒

select current_timestamp;   
输出:2024-01-01 15:32:22.402

5)month:获取日期中的月

语法:month (string date) 
返回值:int 
select month('2022-08-08 08:08:08');
输出:8

6)day:获取日期中的日

语法:day (string date) 
返回值:int 
select day('2022-08-08 08:08:08')    
输出:8

7)hour:获取日期中的小时

语法:hour (string date) 
返回值:int 
select hour('2022-08-08 08:08:08');   
输出:8

8)datediff:两个日期相差的天数(结束日期减去开始日期的天数)

语法:datediff(string enddate, string startdate) 
返回值:int 
select datediff('2021-08-08','2022-10-09');     
输出:-427

9)date_add:日期加天数

语法:date_add(string startdate, int days) 
返回值:string 	
说明:返回开始日期 startdate 增加 days 天后的日期
select date_add('2022-08-08',2);   
输出:2022-08-10

10)date_sub:日期减天数

语法:date_sub (string startdate, int days) 
返回值:string 
说明:返回开始日期startdate减少days天后的日期。
select date_sub('2022-08-08',2);    
输出:2022-08-06

11)date_format:将标准日期解析成指定格式字符串

select date_format('2022-08-08','yyyy年-MM月-dd日')   
输出:2022-08-08

1.2.5 流程控制函数

1)case when:条件判断函数

语法一:case when a then b [when c then d]* [else e] end 
返回值:T 
说明:如果a为true,则返回b;如果c为true,则返回d;否则返回 e 

语法二: case a when b then c [when d then e]* [else f] end 
返回值: T 
说明:如果a等于b,那么返回c;如果a等于d,那么返回e;否则返回f 

2)if: 条件判断,类似于Java中三元运算符

语法:if(boolean testCondition, T valueTrue, T valueFalseOrNull)
返回值:T 
说明:当条件testCondition为true时,返回valueTrue;否则返回valueFalseOrNull

(1)条件满足,输出正确

select if(10 > 5,'正确','错误'); 
输出:正确

(2)条件满足,输出错误

select if(10 < 5,'正确','错误');
输出:错误

1.2.6 集合函数

1)size:集合中元素的个数

select size(friends) from test;  --2/2  每一行数据中的friends集合里的个数

2)map:创建map集合

语法:map (key1, value1, key2, value2, …) 
说明:根据输入的key和value对构建map类型

3)map_keys: 返回map中的key

select map_keys(map('xiaohai',1,'dahai',2));
输出:["xiaohai","dahai"]

4)map_values: 返回map中的value

select map_values(map('xiaohai',1,'dahai',2));
输出:[1,2]

5)array 声明array集合

语法:array(val1, val2, …) 
说明:根据输入的参数构建数组array类
select array('1','2','3','4');
输出:["1","2","3","4"]

6)array_contains: 判断array中是否包含某个元素

select array_contains(array('a','b','c','d'),'a');
输出:true

7)sort_array:将array中的元素排序

select sort_array(array('a','d','c'));
输出:["a","c","d"]

8)struct声明struct中的各属性

语法:struct(val1, val2, val3, …) 
说明:根据输入的参数构建结构体struct类
select struct('name','age','weight');
输出:
{"col1":"name","col2":"age","col3":"weight"}

9)named_struct声明struct的属性和值

select named_struct('name','xiaosong','age',18,'weight',80);
输出:{"name":"xiaosong","age":18,"weight":80}

1.2.7 案列演示

1.2.7.1 数据准备

1)表结构
以下是根据文中内容整理的表格,以markdown格式表示:

namesexbirhtdayhiredatejobsalarybonusfriendschildren
张无忌1980/02/122022/08/09销售300012000[阿朱, 小昭]{张小无:8, 张小忌:9}
赵敏1982/05/182022/09/10行政90002000[阿三, 阿四]{赵小敏:8}
黄蓉1982/04/132022/06/11行政12000Null[东邪, 西毒]{郭芙:5, 郭襄:4}

2)建表语句

create  table  employee(
    name string,  --姓名
    sex  string,  --性别
    birthday string, --出生年月
    hiredate string, --入职日期
    job string,   --岗位
    salary double, --薪资
    bonus double,  --奖金
    friends array<string>, --朋友
    children map<string,int> --孩子
)

3)插入数据

insert into employee  
  values('张无忌','男','1980/02/12','2022/08/09','销售',3000,12000,array('阿朱','小昭'),map('张小无',8,'张小忌',9)),
        ('赵敏','女','1982/05/18','2022/09/10','行政',9000,2000,array('阿三','阿四'),map('赵小敏',8)),
        ('宋青书','男','1981/03/15','2022/04/09','研发',18000,1000,array('王五','赵六'),map('宋小青',7,'宋小书',5)),
        ('周芷若','女','1981/03/17','2022/04/10','研发',18000,1000,array('王五','赵六'),map('宋小青',7,'宋小书',5)),
        ('郭靖','男','1985/03/11','2022/07/19','销售',2000,13000,array('南帝','北丐'),map('郭芙',5,'郭襄',4)),
        ('黄蓉','女','1982/12/13','2022/06/11','行政',12000,null,array('东邪','西毒'),map('郭芙',5,'郭襄',4)),
        ('杨过','男','1988/01/30','2022/08/13','前台',5000,null,array('郭靖','黄蓉'),map('杨小过',2)),
        ('小龙女','女','1985/02/12','2022/09/24','前台',6000,null,array('张三','李四'),map('杨小过',2))
1.2.7.2 需求

1)统计每个月的入职人数

select
    year(replace(hiredate,'/','-')) as year,
    month(replace(hiredate,'/','-')) as month,
    count(*) as count
from employee
group by year(replace(hiredate,'/','-')),month(replace(hiredate,'/','-'));

结果
2)查询每个人的孩子的姓名

select 
name,
map_keys(children) ch_name
from 
employee; 

结果

1.3 高级聚合函数

多进一出 (多行传入,一个行输出)。
1)普通聚合 count/sum…
2)collect_list 收集并形成list集合,结果不去重

select 
  sex,
  collect_list(job)
from
  employee
group by 
  sex
  
结果:
女	["行政","研发","行政","前台"]["销售","研发","销售","前台"]

3)collect_set 收集并形成set集合,结果去重

select 
  sex,
  collect_set(job)
from
  employee
group by 
  sex
  
结果:
女	["行政","研发","前台"]["销售","研发","前台"]

1.3.1 案例演示

1)每个月的入职人数以及姓名

select
  month(replace(hiredate,'/','-')) as month,
  count(*) as cn,
  Collect_list(name) as name_list
from
  employee
group by
  month(replace(hiredate,'/','-'))

结果

1.4 炸裂函数

1.4.1 概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4.2 案例演示

1.4.2.1 数据准备

1)表结构
以下是根据文中内容整理的表格,以markdown格式表示:

moviecategory
《疑犯追踪》悬疑,动作,科幻,剧情
《Lie to me》悬疑,警匪,动作,心理,剧情
《战狼2》战争,动作,灾难

2)建表语句

create table movie_info(
    movie string,     --电影名称
    category string   --电影分类
) 
row format delimited fields terminated by "\t";

3)装载语句

insert overwrite table movie_info
values ("《疑犯追踪》", "悬疑,动作,科幻,剧情"),
       ("《Lie to me》", "悬疑,警匪,动作,心理,剧情"),
       ("《战狼2》", "战争,动作,灾难");
1.4.2.2 需求

1)根据上述电影信息表,统计各分类的电影数量

select
    cate,
    count(*)
from
(
    select
    movie,
    split(category,',') category
    from movie_info
) t1 lateral view explode(category) tmp as cate
group by cate;

结果

1.5 窗口函数(开窗函数)

1.5.1 概述

  • 窗口函数:能为每行数据划分一个窗口,然后对窗口范围内的数据进行计算,最后将计算结果返回给该行数据。
  • 窗口函数的语法主要包括”窗口“和”函数“两部分。其中”窗口“用于定义计算范围,”函数“用于定义计算逻辑。
  • 窗口范围的定义分为两种类型,一种是基于的,一种是基于的。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 窗口——分区
    定义窗口范围时,可以指定分区字段,每个分区单独划分窗口。
    在这里插入图片描述
  • 窗口——缺省
    • over()中的三部分内容partition by、order by、(rows/range) between … and …均可省略不写。
    • partition by省略不写,表示不分区
    • order by省略不写,表示不排序
    • (rows|range) between …and …省略不写,则使用其默认值,默认值如下若over()中包含order by.,则默认值为range between unbounded preceding and current row.
    • 若over()中不包含order by.,则默认值为rows between unbounded preceding and unbounded following.
      在这里插入图片描述

1.5.2 常用窗口函数

按照功能,常用窗口可划分为如下几类:聚合函数、跨行取值函数、排名函数。

1)聚合函数
max:最大值。
min:最小值。
sum:求和。
avg:平均值。
count:计数。

2)跨行取值函数
(1)lead和lag

在这里插入图片描述
注:lag和lead函数不支持自定义窗口。

(2)first_value和last_value

在这里插入图片描述
(3)排名函数
在这里插入图片描述
注:rank 、dense_rank、row_number不支持自定义窗口。

1.5.3 案例演示

1.5.3.1 数据准备

1)表结构

order_iduser_iduser_nameorder_dateorder_amount
11001小元2022-01-0110
21002小海2022-01-0215
31001小元2022-02-0323
41002小海2022-01-0429
51001小元2022-01-0546

2)建表语句

create table order_info
(
    order_id     string, --订单id
    user_id      string, -- 用户id
    user_name    string, -- 用户姓名
    order_date   string, -- 下单日期
    order_amount int     -- 订单金额
);

3)装载语句

insert overwrite table order_info
values ('1', '1001', '小元', '2022-01-01', '10'),
       ('2', '1002', '小海', '2022-01-02', '15'),
       ('3', '1001', '小元', '2022-02-03', '23'),
       ('4', '1002', '小海', '2022-01-04', '29'),
       ('5', '1001', '小元', '2022-01-05', '46'),
       ('6', '1001', '小元', '2022-04-06', '42'),
       ('7', '1002', '小海', '2022-01-07', '50'),
       ('8', '1001', '小元', '2022-01-08', '50'),
       ('9', '1003', '小辉', '2022-04-08', '62'),
       ('10', '1003', '小辉', '2022-04-09', '62'),
       ('11', '1004', '小猛', '2022-05-10', '12'),
       ('12', '1003', '小辉', '2022-04-11', '75'),
       ('13', '1004', '小猛', '2022-06-12', '80'),
       ('14', '1003', '小辉', '2022-04-13', '94');

1.5.3.2 需求

1)统计每个用户截至每次下单的累积下单总额

order_iduser_iduser_nameorder_dateorder_amountsum_so_far
11001小元2022-01-011010
51001小元2022-01-054656
81001小元2022-01-0850106
31001小元2022-02-0323129
61001小元2022-04-0642171
21002小海2022-01-021515
41002小海2022-01-042944
71002小海2022-01-075094
91003小辉2022-04-086262
101003小辉2022-04-0962124
121003小辉2022-04-1175199
141003小辉2022-04-1394293
111004小猛2022-05-101212
131004小猛2022-06-128092
select
    order_id,
    user_id,
    user_name,
    order_date,
    order_amount,
    sum(order_amount) over(partition by user_id order by order_date rows between unbounded preceding and current row) sum_so_far
from order_info;

结果
2)统计每个用户截至每次下单的当月累积下单总额

select
    order_id,
    user_id,
    user_name,
    order_date,
    order_amount,
    sum(order_amount) over (partition by user_id,substring(order_date,1,7) order by order_date rows between unbounded preceding and current row )sum_so_far
from order_info;

结果
3)为每个用户的所有下单记录按照订单金额进行排名

select
    order_id,
    user_id,
    user_name,
    order_date,
    order_amount,
    rank() over (partition by user_id order by order_amount desc) rk,
    dense_rank() over (partition by user_id order by order_amount desc) drk,
    row_number() over (partition by user_id order by order_amount desc) rn
from order_info;

结果

1.6 自定义函数

1)Hive自带了一些函数,比如:max/min等,但是数量有限,自己可以通过自定义UDF来方便的扩展。
2)当Hive提供的内置函数无法满足你的业务处理需要时,此时就可以考虑使用用户自定义函数(UDF:user-defined function)。
3)根据用户自定义函数类别分为以下三种:
(1)UDF(User-Defined-Function)
一进一出。
(2)UDAF(User-Defined Aggregation Function)
用户自定义聚合函数,多进一出。
类似于:count/max/min
(3)UDTF(User-Defined Table-Generating Functions)
用户自定义表生成函数,一进多出。
如lateral view explode()

1.7 自定义UDF函数

0)需求
自定义一个UDF实现计算给定基本数据类型的长度,例如:

select my_len("abcd");

1)创建一个Maven工程Hive
2)导入依赖(在pom.xml中)

<dependencies>
	<dependency>
		<groupId>org.apache.hive</groupId>
		<artifactId>hive-exec</artifactId>
		<version>3.1.3</version>
	</dependency>
</dependencies>

3)创建一个类

package com.atguigu.hive.udf;

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;

/**
 * 我们需计算一个要给定基本数据类型的长度
 */
public class MyUDF extends GenericUDF {
    /**
     * 判断传进来的参数的类型和长度
     * 约定返回的数据类型
     */
    @Override
    public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {

        if (arguments.length !=1) {
            throw  new UDFArgumentLengthException("please give me  only one arg");
        }

        if (!arguments[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){
            throw  new UDFArgumentTypeException(1, "i need primitive type arg");
        }

        return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
    }

    /**
     * 解决具体逻辑的
     */
    @Override
    public Object evaluate(DeferredObject[] arguments) throws HiveException {

        Object o = arguments[0].get();
        if(o==null){
            return 0;
        }

        return o.toString().length();
    }

    @Override
    // 用于获取解释的字符串
    public String getDisplayString(String[] children) {
        return "";
    }
}

4)创建临时函数
(1)打成jar包上传到服务器/opt/module/hive/datas/myudf.jar
(2)将jar包添加到hive的classpath,临时生效

add jar /opt/module/hive/datas/myudf.jar;

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

create temporary function my_len 
as "com.yudan.hive.udf.MyUDF";

(4)即可在hql中使用自定义的临时函数

select 
    ename,
    my_len(ename) ename_len 
from emp;

(5)删除临时函数

drop temporary function my_len;

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

4)创建永久函数
(1)创建永久函数
注意:因为add jar本身也是临时生效,所以在创建永久函数的时候,需要制定路径(并且因为元数据的原因,这个路径还得是HDFS上的路径)。

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

(2)即可在hql中使用自定义的永久函数

select 
    ename,
    my_len2(ename) ename_len 
from emp;

(3)删除永久函数

drop function my_len2;

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

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

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

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值