系统内置函数

查看系统自带的函数:

  1. show functions;

显示自带的函数的用法:

  1. desc function upper;

详细显示自带的函数的用法:

  1. desc function extended upper;

Hive的函数分为三类:

  • UDF:一行输入对应一行输出,例如:upper这类的字段转换
  • UDAF:多行输入对应一行输出(聚合函数),例如:maxcount这类聚合函数
  • UDTF:一行输入对应多行输出(炸裂函数),例如:explode将一个Array、Map字段拆分成多行返回

    常用函数

    普通类型字段函数

    大致同Oracle的各个函数: ```sql — nvl空值转换 select nvl(null, ‘a’)

— case when then else end select deptno, case deptno when 10 then ‘aaa’ when 20 then ‘bbb’ else ‘ccc’ end from dept;

— 或者 select deptno, case when deptno < 30 then ‘aaa’ when deptno < 40 then ‘bbb’ else ‘ccc’ end from dept

— concat 拼接字符串 select concat(dname, ‘,’, loc), o.* from dept o;

— 其他的,upper、lower、max、min、count、avg、abs、length、trim、lpad、rpad等函数同oracle select lpad(‘aa’, 8, ‘0’); — 位数不足8位时,向左边补零

— 取整: — ceil:向上取整 — floor:向下取整 — round: 四舍五入 select floor(1.25);

  1. 其他常用函数:
  2. ```sql
  3. -- if 函数
  4. select
  5. deptno,
  6. -- if(判断条件, 判断为真, 否则)
  7. if(deptno > 30, 'aaa', 'bbb')
  8. from dept;
  9. -- concat_ws 拼接字符串:把分隔符提到最前面
  10. -- 等同于 select concat(ename, ',', job, ',', hiredate) , e.* from emp e;
  11. select concat_ws(',', ename, job, hiredate) , e.* from emp e;
  12. -- concat_ws函数 也可以将ARRAY类型的字段的每个元素使用指定分隔符拼接成字符串
  13. select concat_ws(',', friends), t.* from test3 t;
  14. -- split:将字符串转换成数组
  15. select split(name,'n'), s.* from student1 s;
  16. -- 获取当前时间的时间戳(长整形数字,相对于unix时间的偏移量)
  17. select unix_timestamp();
  18. -- 将日期字符串转换成时间戳的长整型
  19. select unix_timestamp('2020-02-01', 'yyyy-MM-dd');
  20. -- 将相对于unix时间偏移量的长整型数字转换为日期格式
  21. select from_unixtime(1580515200);
  22. select from_unixtime(1580515200, 'yyyy/MM/dd');
  23. -- 获取当前日期
  24. select current_date();
  25. -- 获取当前时间戳
  26. select current_timestamp();
  27. -- 获取时间戳中的日期
  28. select to_date(current_timestamp());
  29. select to_date('2022-01-01 10:12:00');
  30. -- 也可以通过 year、month、day、hour、minute、second、weekofyear、dayofmonth 等相关函数获取年月日时分秒
  31. select year('2022-01-01 10:12:00');
  32. -- 两个日期之间的月数
  33. select months_between('2022-01-01 10:12:00','2021-10-05 10:12:00');
  34. -- 几个月后/前
  35. select add_months('2022-01-01 10:12:00', -2);
  36. select add_months('2022-01-01 10:12:00', 2, 'yyyy/MM/dd');
  37. -- 计算两个日期之间相差的天数
  38. select datediff('2022-01-01 10:12:00', '2021-10-24 10:12:00');
  39. -- 日期加上n天
  40. select date_add('2022-01-01 10:12:00', 2);
  41. -- 获取月末的日期
  42. select last_day('2022-01-01 10:12:00');
  43. -- 日期格式化
  44. select date_format('2022-01-01 10:12:00', 'yyyy/MM/dd');
  45. -- 正则表达式匹配替换
  46. select regexp_replace('2020/10/25', '/', '-')

集合操作

示例:

  1. -- 获取ArrayMap字段中的元素个数
  2. select size(friends),size(children) from test3;
  3. -- 获取map中的所有key
  4. select map_keys(children) from test3;
  5. -- 获取map中的所有value
  6. select map_values(children) from test3;
  7. -- 判断array中是否包含某个元素
  8. select array_contains(friends,'Jack') from test3;
  9. -- array中的元素进行排序
  10. select sort_array(friends) from test3;

行转列函数

行转列:将多行数据的某个字段合并成一个值

  1. -- collect_set:将多行数据中的某个字段合并拼接成一个数组(行转列)
  2. -- collect_set只接收基本数据类型,且会将字段的值进行去重,然后产生Array类型的字段
  3. select collect_set(deptno) from dept;
  4. -- 功能大致同collect_set,但是collect_list不会进行去重,collect_set会进行去重
  5. select collect_list(deptno) from dept;

示例:

  1. -- 将相同部门、相同工作的雇员拼接到一起
  2. select
  3. concat_ws('|',collect_list(ename)), -- 使用collect_list聚合,然后使用concat_ws将数组元素拼接成字符串
  4. concat(job,',',deptno) -- 分组字段jobdeptno,将分组字段使用逗号拼接到一起
  5. from emp
  6. group by
  7. job,deptno;

列转行函数

列转行:将一个值拆分成多行进行展示

  1. -- explode:将Array字段或者Map字段拆分成多行(列转行)
  2. -- 将数组元素拆分成多行返回,每行只存储一个元素
  3. select explode(friends) from test3;
  4. -- map拆分成多行返回,返回两个字段,一个key字段,一个value字段,每行存储一对key-value
  5. select explode(children) from test3;
  6. -- lateral view :侧写
  7. -- 直接select explode函数时,不能再同时查询其他字段,否则会报错
  8. -- 如果想要同时关联展示表中的其他字段,可以使用 lateral view 进行侧写
  9. select
  10. name, -- 可以正常查询其他字段
  11. friends,
  12. children,
  13. perFriend -- 查询侧写字段(即展开成多行的friends字段),最后效果类似联合查询
  14. from
  15. test3
  16. lateral view
  17. explode(friends) perFriend as perFriend; -- 通过lateral view 将展开了的friends字段进行侧写,将输出的结果列起别名为perFriend
  18. -- 如果是map,会展开成两列,所以侧写时,as语句后要有两个字段名
  19. select
  20. name,
  21. friends,
  22. children,
  23. perChildName, -- 展开了的chidren中每个key
  24. perChildAge -- 展开了的chidren中每个value
  25. from
  26. test3
  27. lateral view
  28. explode(children) perChildInfo as perChildName,perChildAge -- map会展开成两列,所以as语句后要有两个字段名

Over窗口函数

函数说明

over():指定分析函数工作的数据窗口大小,这个数据窗口大小可能会随着行的变化而变化。同Oracle的over()

其中over()函数内可以使用的关键字:

  • CURRENT ROW:当前行
  • n PRECEDING:往前n行数据
  • n FOLLOWING:往后n行数据
  • UNBOUNDED:没有边界的
    • UNBOUNDED RRECEDING:从前面的起点
    • UNBOUNDED FOLLOWING:到后面的终点

示例

查询10号部门的每个雇员,以及总人数:

  1. select
  2. ename,
  3. -- 每个雇员的数据行中,都会显示10号部门的总人数
  4. count(*) over() -- 此处count(*)的分组条件是over开窗内的分组条件,因为此处over内容为空,所以就直接统计了总人数
  5. from emp
  6. where deptno = 10;

查询10号部门的每个雇员,以及和该雇员从事相同工作的总人数:

  1. select
  2. ename,
  3. deptno,
  4. job,
  5. count(*) over(partition by job) -- 此处在over内增加分组条件 job,就会按job进行分组统计
  6. from emp
  7. where deptno = 30;

查询每个雇员信息,并且在雇员后面展示该雇员所在部门的薪资累加:同一个部门的1号雇员展示自己的薪资,2号雇员展示1号和2号的累加,3号雇员展示1/2/3雇员的累加,以此类推

  1. select
  2. e.*,
  3. sum(sal) over(
  4. partition by deptno
  5. order by deptno,empno) -- 加入 order by ,使其按顺序进行累加展示(默认统计的就是从起点数据到当前行)
  6. from emp e
  7. order by deptno,empno;
  8. -- 或者完整版写法:
  9. select
  10. e.*,
  11. sum(sal) over(
  12. partition by deptno
  13. order by deptno,empno
  14. rows between unbounded preceding and current row) -- 累加时,只累加从起点到当前行的数据,不计算后面的数据
  15. from emp e
  16. order by deptno,empno

查询每个雇员信息,并在雇员后展示该雇员所在部门的薪资汇总:汇总该雇员前面1位雇员 (如果存在)+ 自己 + 自己后面1位雇员(如果存在)

  1. select
  2. e.*,
  3. sum(sal) over(
  4. partition by deptno
  5. order by deptno,empno
  6. rows between 1 preceding and 1 following) -- 累加时:从自己的前一行开始,到自己的后一行
  7. from emp e
  8. order by deptno,empno

注意事项

如果order by排序的数据有重复的,hive、oracle会将这两行数据认为是同一行数据。

例如:有以下数据

  1. 1
  2. 2
  3. 3
  4. 3 // 同时存在两条数据为3
  5. 4
  6. 5

此时使用 SQL 查询:

  1. select
  2. id,
  3. sum(id) over(order by id)
  4. from test

得到的结果为:

  1. 1 1
  2. 2 3
  3. 3 9 // 两条id=3的数据的sum值都是9
  4. 3 9
  5. 4 13
  6. 5 18

总结

带有over()窗口的SQL:

  • 先由where条件限定结果集的范围
  • 然后partition by决定如何进行划定窗口分区,类似group by
  • 然后在分区内,再由order by决定数据的排序,rows between决定数据计算的范围。
    如果只有order by,没有rows between,则默认的数据范围是从该分区的起点到当前行

其他搭配over使用的函数

Oracle、Hive都有下列函数。

LAG(col, n, default_val):往前第n行数据

LEAD(col, n, default_val):往后第n行数据

NTILE(n):把有序窗口的行分发到指定数量的组中,各个组有编号,编号从1开始,对于每一行,NTILE返回此行所属的组的编号。n必须为int类型

示例:

获取同一个部门中,上一个雇员的薪资,如果该雇员为第一个,则返回0

  1. select
  2. e.*,
  3. -- 使用lag获取前1个雇员的薪资,lag(要获取的字段, 往前第几个, 如果不存在则返回的默认值/字段)
  4. -- 默认值也可以不设置,如果不设置则不存在时返回值为null
  5. -- 默认值如果设置的是个字段,则取的是当前行的字段(因为前面那行不存在,所以是直接本身的字段)
  6. lag(sal, 1, 0) over (partition by deptno order by empno)
  7. from emp e
  8. order by deptno, empno;

将雇员按照雇佣时间分为5组:

  1. select
  2. e.*,
  3. ntile(5) over(order by hiredate) -- 按照雇佣时间排序,均分为5组(如果不能均分,最后一组的数量会少一些)
  4. from emp e
  5. order by hiredate

窗口内排序

搭配over()使用的排序的相关函数:(Oracle、Hive相同)

  • rank():排序时如果数值相同会重复,总数不变
  • DENSE_RANK():排序时如果数值相同会重复,总数会减少
  • ROW_NUMBER():会根据顺序计算(即最后显示的是行号)

示例:
将每个部门的雇员按薪资从高到低排序:

  1. select
  2. e.*,
  3. rank() over(partition by deptno order by sal desc), -- 使用 rank() 排序
  4. dense_rank() over(partition by deptno order by sal desc), -- 使用 dense_rank() 排序
  5. row_number() over(partition by deptno order by sal desc) -- 使用row_number()排序
  6. from emp e
  7. order by deptno, sal desc

其中deptNo=30的雇员薪资有重复的,三种不同的排序方式的结果为:

SAL rank dense_rank row_number
2850 1 1 1
1600 2 2 2
1500 3 3 3
1250 4 4 4
1250 4 4 5
950 6 5 6

数据中存在两条SAL=1250的数据,rankdense_rank对这两条数据的排序结果都是4。但是对于下一条数据(SAL=950),rank的排序号是在两个1250后的6,而dense_rank的排序号是连续的5

Grouping Sets 多维分析函数

Grouping Sets用于多维分析使用,等价于将几个group by语句进行union

Grouping Sets中传入统计的维度,可以传入多个维度,按逗号分隔

例如:

deptNo + job 进行统计

  1. select deptno,job,sum(sal) from emp group by deptno ,job
  2. grouping sets((deptno,job));
  3. -- 等价于
  4. select deptno,job,sum(sal) from emp group by deptno ,job

deptNo + job 统计,并按deptNo进行统计:

  1. select deptno,job,sum(sal) from emp group by deptno ,job
  2. grouping sets((deptno,job), deptNo);
  3. -- 等价于
  4. select deptno,job,sum(sal) from emp group by deptno ,job
  5. union
  6. select deptno,sum(sal) from emp group by deptno

deptNo + job统计,按deptNo统计,统计合计:

  1. select deptno,job,sum(sal) from emp
  2. group by deptno ,job
  3. grouping sets((deptno,job), deptNo, ());
  4. -- 等价于
  5. select deptno,job,sum(sal) from emp group by deptno ,job
  6. union
  7. select deptno,sum(sal) from emp group by deptno
  8. union
  9. select sum(sal) from emp

自定义函数

当Hive提供的内置函数无法满足需求时,就可以考虑使用自定义函数 UDF (user-defined function)。

用户自定义函数分为三种:

  • UDF(User-Defined-Function):单行输入单行输出
  • UDAF(User-Defined Aggregation Function):聚合函数,多行输入单行输出
  • UDTF(User-Defined Table-Generating Function):炸裂函数,单行输入多行输出

开发自定义函数步骤

开发步骤:

  1. 继承Hive提供的类
    1. org.apache.hadoop.hive.ql.udf.generic.GenericUDF
    2. org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
  1. 实现类中的抽象方法
  2. 在Hive的命令行窗口创建函数。
    添加jar:

    1. add jar 路径


    创建function:

  3. 在hive的命令行窗口删除函数

    1. drop [temporary] function [if exists] [dbname.]function_name;

自定义UDF函数示例

创建maven项目,引入依赖:

  1. <dependency>
  2. <groupId>org.apache.hive</groupId>
  3. <artifactId>hive-exec</artifactId>
  4. <!-- 对应hive版本的依赖 -->
  5. <version>3.1.3</version>
  6. </dependency>

编写自定义函数MyUDF,用于计算字符串长度:

  1. package com.study.udf;
  2. import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
  3. import org.apache.hadoop.hive.ql.metadata.HiveException;
  4. import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
  5. import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
  6. import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector;
  7. import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorConverter;
  8. import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
  9. /**
  10. * 自定义函数:返回字符串长度
  11. * @author tengyer 2022/08/18 15:22
  12. */
  13. public class MyUDF extends GenericUDF {
  14. /**
  15. * 初始化操作,可以用于做一些校验等
  16. * @param arguments 传入的参数
  17. */
  18. @Override
  19. public ObjectInspector initialize(ObjectInspector[] arguments) throws UDFArgumentException {
  20. if(arguments.length != 1) {
  21. throw new UDFArgumentException("参数个数不为1");
  22. }
  23. return PrimitiveObjectInspectorFactory.javaIntObjectInspector; // 最后函数的返回值类型,此处是返回字符串长度,所以设置为Int类型
  24. }
  25. /**
  26. * 运算:获取输入的字符串的长度
  27. * @param arguments 输入的参数
  28. */
  29. @Override
  30. public Object evaluate(DeferredObject[] arguments) throws HiveException {
  31. if(arguments[0] == null) {
  32. return 0;
  33. }
  34. // 获取输入的字符串
  35. String inputArgument = arguments[0].get().toString();
  36. return inputArgument.length(); // 返回字符串的长度
  37. }
  38. /**
  39. * 用于展示执行计划用,自定义函数直接返回空就行
  40. */
  41. @Override
  42. public String getDisplayString(String[] strings) {
  43. return "";
  44. }
  45. }

然后使用maven的package打成jar包。

将 jar 包重命名成一个简单的名称,然后上传到 Hive 的 lib 目录。(实际上,上传到Linux的任意路径都可以,但是习惯上一般上传到Hive的lib目录)。

Hive不会自动加载lib下的jar,只有重启Hive时才会重新加载。如果想要立即生效,可以在Hive中手动加入该jar:

  1. add jar /opt/module/hive-3.1.3/lib/MyUDF.jar

然后便可以创建函数:

  1. create temporary function my_len as "com.study.udf.MyUDF";

测试自定义函数:

  1. select my_len('aaa');

自定义UDTF函数示例

编写Java:

  1. package com.study.udf;
  2. import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
  3. import org.apache.hadoop.hive.ql.metadata.HiveException;
  4. import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
  5. import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
  6. import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
  7. import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
  8. import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
  9. import org.apache.logging.log4j.core.appender.routing.Route;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. /**
  13. * 自定义炸裂函数:将一个字符串按逗号拆分,分成多个数据行
  14. * 例如:aaa,bbb,ccc,ddd 炸裂成4行数据
  15. * @author tengyer 2022/08/18 15:53
  16. */
  17. public class MyUDTF extends GenericUDTF {
  18. /**
  19. * 初始化方法
  20. * @param argOIs
  21. * @return
  22. * @throws UDFArgumentException
  23. */
  24. @Override
  25. public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
  26. // 输出数据的列名、类型都是集合,因为可以从一个字段值炸裂出多个列。例如可以将一个map值explode出:一个key列、一个value列
  27. // 所以炸裂函数可以输出多个列。输出的列名、类型都需要是集合
  28. // 输出数据的默认列名,可以被SQL中的as别名覆盖掉
  29. List<String> fieldNames = new ArrayList<>();
  30. fieldNames.add("word"); // 此处我们只需要将字符串拆分出一个列即可,列名叫word
  31. // 输出数据的类型
  32. List<ObjectInspector> fieldOIs = new ArrayList<>();
  33. fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); // 我们的字符串拆分出来的结果列的类型依然是字符串
  34. return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
  35. }
  36. /**
  37. * 处理输入数据:
  38. * @param args 以逗号为分隔符的字符串,例如aaa,bbb,ccc,ddd
  39. * @throws HiveException
  40. */
  41. @Override
  42. public void process(Object[] args) throws HiveException {
  43. // 取出输入的数据并按逗号拆分
  44. String input = args[0].toString();
  45. String[] words = input.split(",");
  46. for (String word : words) {
  47. // 输出的结果。因为炸裂的结果可能不止一列,所以是个集合
  48. // 此处我们只拆分成一列,所以只需要add一个数据
  49. List<String> resultWordList = new ArrayList<>();
  50. resultWordList.add(word);
  51. // 通过forward方法输出
  52. forward(resultWordList);
  53. }
  54. }
  55. /**
  56. * 结束时关闭资源
  57. * @throws HiveException
  58. */
  59. @Override
  60. public void close() throws HiveException {
  61. // 这里我们没有打开的IO流等资源,所以不需要关闭资源
  62. }
  63. }

使用maven编译打包,上传Linux服务器。

通过add jar 引入该jar,创建函数:

  1. create temporary function my_split as "com.study.udf.MyUDTF";

测试函数:

  1. select my_split('aaa,bbb,ccc,ddd');

最终输出一个word列,将字符串拆分成该列的多行数据。

自定义UDTF函数示例2

将字符串炸裂成多列:

  1. package com.study.udf;
  2. import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
  3. import org.apache.hadoop.hive.ql.metadata.HiveException;
  4. import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
  5. import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
  6. import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
  7. import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
  8. import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. /**
  12. * 自定义炸裂函数:将一个字符串按逗号拆分,分成多个数据行;每行再按冒号拆分成两个字段
  13. * 例如 aaa:123,bbb:456,ccc:789,其中按逗号拆分成多行,每行再按冒号拆分成多列
  14. * @author tengyer 2022/08/18 15:53
  15. */
  16. public class MyUDTF2 extends GenericUDTF {
  17. /**
  18. * 初始化方法
  19. * @param argOIs
  20. * @return
  21. * @throws UDFArgumentException
  22. */
  23. @Override
  24. public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
  25. // 输出数据的默认列名,可以被SQL中的as别名覆盖掉
  26. List<String> fieldNames = new ArrayList<>();
  27. fieldNames.add("word_key"); // 冒号前面的字符炸裂的字段名
  28. fieldNames.add("word_value"); // 冒号后面的字符炸裂的字段名
  29. // 输出数据的类型
  30. List<ObjectInspector> fieldOIs = new ArrayList<>();
  31. fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); // 冒号前面的字符类型是字符串
  32. fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector); // 冒号后面的字符类型是字符串
  33. return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);
  34. }
  35. /**
  36. * 处理输入数据:
  37. * @param args 以逗号+冒号为分隔符的字符串,例如 aaa:123,bbb:456,ccc:789
  38. * @throws HiveException
  39. */
  40. @Override
  41. public void process(Object[] args) throws HiveException {
  42. // 取出输入的数据并按逗号拆分
  43. String input = args[0].toString();
  44. String[] entries = input.split(","); // 按逗号拆分成多行
  45. for (String entry : entries) {
  46. // 每行按冒号拆分成多个字段
  47. String[] result = entry.split(":");
  48. List<String> resultWordList = new ArrayList<>();
  49. resultWordList.add(result[0]);
  50. resultWordList.add(result[1]);
  51. // 通过forward方法输出
  52. forward(resultWordList);
  53. }
  54. }
  55. /**
  56. * 结束时关闭资源
  57. * @throws HiveException
  58. */
  59. @Override
  60. public void close() throws HiveException {
  61. // 这里我们没有打开的IO流等资源,所以不需要关闭资源
  62. }
  63. }

指定为自定义函数:

  1. create temporary function my_split2 as "com.study.udf.MyUDTF2";

查询:

  1. select my_split2('aaa:123,bbb:456,ccc:789');

查询结果:

  1. word_key word_value
  2. aaa 123
  3. bbb 456
  4. ccc 789