Hive 从 0 到 1 学习 —— 第八章 Hive 查询函数

1. 系统内置函数

  1. 查看系统自带的函数

    hive> show functions;
    
  2. 显示自带的函数的用法

    hive> desc function upper;
    
  3. 详细显示自带的函数的用法

    hive> desc function extended upper;
    

2. 常用函数

2.1 指标函数

  1. 求总行数(count)

    hive (default)> select count(*) cnt from emp;
    
  2. 求工资的最大值(max)

    hive (default)> select max(sal) max_sal from emp;
    
  3. 求工资的最小值(min)

    hive (default)> select min(sal) min_sal from emp;
    
  4. 求工资的总和(sum)

    hive (default)> select sum(sal) sum_sal from emp; 
    
  5. 求工资的平均值(avg)

    hive (default)> select avg(sal) avg_sal from emp;
    

2.2 collect_set 函数

函数只接受基本数据类型,它的主要作用是将某字段的值进行去重汇总,产生array类型字段。

把同一分组的不同行的数据聚合成一个集合

hive (default)> select collect_set(ename),avg(sal) avg_sal from emp group by deptno;

2.3 日期处理函数

  1. date_format函数(根据格式整理日期)

    hive (default)> select date_format('2020-12-19','yyyy-MM');
    2020-12
    
  2. date_add函数(加减日期)

    hive (default)> select date_add('2019-12-19',-1);
    2020-12-18
    hive (default)> select date_add('2019-12-19',1);
    2020-12-20
    
  3. next_day函数

    1. 取当前天的下一个周一

      hive (default)> select next_day('2020-12-19','MO');
      2020-12-21
      

      说明:星期一到星期日的英文(Monday,Tuesday、Wednesday、Thursday、Friday、Saturday、Sunday)

    2. 取当前周的周一

      hive (default)> select date_add(next_day('2020-12-19','MO'),-7);
      2020-12-14
      
  4. last_day函数(求当月最后一天日期)

    hive (default)> select last_day('2020-12-19');
    2020-12-31
    

2.4 字符串连接函数

  1. concat 函数

    返回输入字符串连接后的结果,支持任意个输入字符串;

    hive (default)> select concat ('2020-12-19','_','2020-12-20');
    2020-12-19_2020-12-20
    
  2. concat_ws 函数

    它是一个特殊形式的 CONCAT()。第一个参数剩余参数间的分隔符。分隔符可以是与剩余参数一样的字符串。如果分隔符是 NULL,返回值也将为 NULL。这个函数会跳过分隔符参数后的任何 NULL 和空字符串。分隔符将被加到被连接的字符串之间;

    hive (default)> select concat_ws ('_','2020-12-19','2020-12-20');
    2020-12-19_2020-12-20
    

2.5 json 解析函数

hive (default)> select get_json_object(json,'$.mid') mid_id from table_name;

3. 其他常用查询函数

3.1 空字段赋值

  1. 函数说明

    NVL:给值为 NULL 的数据赋值,它的格式是 NVL( string1, replace_with)。它的功能是如果string1为NULL,则NVL函数返回replace_with的值,否则返回string1的值,如果两个参数都为NULL ,则返回NULL。

  2. 数据准备:采用员工表

  3. 查询:如果员工的 comm 为 NULL,则用 -1代替

    hive (default)> select nvl(comm,-1) from emp;
    OK
    _c0
    -1.0
    300.0
    500.0
    -1.0
    1400.0
    -1.0
    -1.0
    -1.0
    -1.0
    0.0
    -1.0
    -1.0
    -1.0
    -1.0
    Time taken: 0.11 seconds, Fetched: 14 row(s)
    
  4. 查询:如果员工的comm为NULL,则用领导id代替

    hive (default)> select nvl(comm,mgr) from emp;
    OK
    _c0
    7902.0
    300.0
    500.0
    7839.0
    1400.0
    7839.0
    7839.0
    7566.0
    NULL
    0.0
    7788.0
    7698.0
    7566.0
    7782.0
    Time taken: 0.115 seconds, Fetched: 14 row(s)
    

3.2 CASE WHEN

  1. 数据准备

    悟空	A	男
    大海	A	男
    宋宋	B	男
    凤姐	A	女
    婷姐	B	女
    婷婷	B	女
    
  2. 需求

    求出不同部门男女各多少人。结果如下:

    A     2       1
    B     1       2
    
  3. 创建hive表并导入数据

    create table emp_sex(
    name string, 
    dept_id string, 
    sex string
    ) 
    row format delimited fields terminated by "\t";
    load data local inpath '/opt/module/datas/emp_sex.txt' into table emp_sex;
    
  4. 按需求查询数据

    select 
      dept_id,
      sum(case sex when '男' then 1 else 0 end) male_count,
      sum(case sex when '女' then 1 else 0 end) female_count
    from 
      emp_sex
    group by
      dept_id;
    

3.3 行转列

  1. 相关函数说明

    CONCAT(string A/col, string B/col…):返回输入字符串连接后的结果,支持任意个输入字符串;

    CONCAT_WS(separator, str1, str2,...):它是一个特殊形式的 CONCAT()。第一个参数是分隔符。分隔符可以是与剩余参数一样的字符串。如果分隔符是 NULL,返回值也将为 NULL。这个函数会跳过分隔符参数后的任何 NULL 和空字符串。分隔符将被加到被连接的字符串之间;

    COLLECT_SET(col):函数只接受基本数据类型,它的主要作用是将某字段的值进行去重汇总,产生array类型字段。

  2. 数据准备

    表 数据准备
name constellation blood_type
孙悟空 白羊座 A
沙僧 射手座 A
唐僧 白羊座 B
猪八戒 白羊座 A
白龙马 射手座 A
  1. 需求

    把星座和血型一样的人归类到一起。结果如下:

    射手座,A	沙僧|白龙马
    白羊座,A	孙悟空|猪八戒
    白羊座,B	唐僧
    
  2. 创建本地constellation.txt,导入数据

    [dwjf321@hadoop102 ~]$ vim /opt/module/datas/constellation.txt
    孙悟空  白羊座  A
    沙僧    射手座  A
    唐僧    白羊座  B
    猪八戒  白羊座  A
    白龙马  射手座  A
    
  3. 创建hive表并导入数据

    create table person_info(
    name string, 
    constellation string, 
    blood_type string) 
    row format delimited fields terminated by "\t";
    load data local inpath '/opt/module/datas/constellation.txt' into table person_info;
    
  4. 按需求查询结果

    select
    	t.base,
    	concat_ws('|', collect_set(t.name)) name
    from
    	(
    	select
    		concat(constellation, ',', blood_type) base, name
    	from
    		person_info ) t
    group by
    	t.base
    

3.4 列转行

  1. 函数说明

    EXPLODE(col):将hive一列中复杂的array或者map结构拆分成多行。

    LATERAL VIEW

    用法:LATERAL VIEW udtf(expression) tableAlias AS columnAlias

    解释:用于和split, explode等UDTF一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。

  2. 数据准备

    表 数据准备
movie category
《疑犯追踪》 悬疑,动作,科幻,剧情
《Lie to me》 悬疑,警匪,动作,心理,剧情
《战狼2》 战争,动作,灾难
  1. 需求

    将电影分类中的数组数据展开。结果如下:

    《疑犯追踪》      悬疑
    《疑犯追踪》      动作
    《疑犯追踪》      科幻
    《疑犯追踪》      剧情
    《Lie to me》   悬疑
    《Lie to me》   警匪
    《Lie to me》   动作
    《Lie to me》   心理
    《Lie to me》   剧情
    《战狼2》        战争
    《战狼2》        动作
    《战狼2》        灾难
    
  2. 创建本地movie.txt,导入数据

    [dwjf321@hadoop102 ~]$ vim /opt/module/datas/movie.txt
    《疑犯追踪》    悬疑,动作,科幻,剧情
    《Lie to me》   悬疑,警匪,动作,心理,剧情
    《战狼2》       战争,动作,灾难
    
  3. 创建 hive 表并导入数据

    create table movie_info(
        movie string, 
        category array<string>) 
    row format delimited fields terminated by "\t"
    collection items terminated by ",";
    load data local inpath '/opt/module/datas/movie.txt' into table movie_info;
    
  4. 按需求查询数据

    select
    	movie,
    	category_name
    from
    	movie_info lateral view explode(category) temp_col as category_name;
    

3.5 窗口函数

  1. 相关函数说明

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

    CURRENT ROW:当前行

    n PRECEDING:往前n行数据

    n FOLLOWING:往后n行数据

    UNBOUNDED:起点

    UNBOUNDED PRECEDING: 表示从前面的起点

    UNBOUNDED FOLLOWING:表示到后面的终点

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

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

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

  2. 数据准备:name,orderdate,cost

    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
    mart,2017-04-08,62
    mart,2017-04-09,68
    neil,2017-05-10,12
    mart,2017-04-11,75
    neil,2017-06-12,80
    mart,2017-04-13,94
    
  3. 需求

    (1)查询在2017年4月份购买过的顾客及总人数

    (2)查询顾客的购买明细及月购买总额

    (3)上述的场景,要将cost按照日期进行累加

    (4)查询顾客上次的购买时间

    (5)查询前20%时间的订单信息

  4. 创建本地business.txt,导入数据

    [dwjf321@hadoop102 ~]$ vim /opt/module/datas/business.txt
    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
    mart,2017-04-08,62
    mart,2017-04-09,68
    neil,2017-05-10,12
    mart,2017-04-11,75
    neil,2017-06-12,80
    mart,2017-04-13,94
    
  5. 创建hive表并导入数据

    create table business(
    name string, 
    orderdate string,
    cost int
    ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',';
    
    load data local inpath '/opt/module/datas/business.txt' into table business;
    
  6. 按需求查询数据

    1. 需求一:查询在 2017 年 4 月份购买过的顾客及总人数

      select
      	name,
      	count(*) over()
      from
      	business
      where
      	substring(orderdate, 0, 7)= '2017-04'
      group by
      	name;
      

      查询结果:

      jack	2
      mart	2
      

      购买过的顾客有:jack 和 mart,总人数是:2。

    2. 需求二:查询顾客的购买明细及月购买总额

      select
      	name,
      	orderdate,
      	cost,
      	sum(cost) over(partition by month(orderdate))
      from
      	business;
      

      查询结果:

      jack	2017-01-01	10	205
      tony	2017-01-02	15	205
      tony	2017-01-04	29	205
      jack	2017-01-05	46	205
      tony	2017-01-07	50	205
      jack	2017-01-08	55	205
      jack	2017-02-03	23	23
      mart	2017-04-13	94	341
      mart	2017-04-08	62	341
      mart	2017-04-09	68	341
      mart	2017-04-11	75	341
      jack	2017-04-06	42	341
      neil	2017-05-10	12	12
      neil	2017-06-12	80	80
      
    3. 需求三:上述的场景,要将cost按照日期进行累加

      select
      	name,
      	orderdate,
      	cost,
      	sum(cost) over() sample1,--所有行相加
      	sum(cost) over(partition by name) sample2,--按 name 分组,组内数据相加
      	sum(cost) over(partition by name order by orderdate) sample3,--按 name 分组,组内数据累加
      	sum(cost) over(partition by name order by orderdate rows between unbounded preceding and current row) sample4,--结果与 sample3一样,由起始行到当前行累加
      	sum(cost) over(partition by name order by orderdate rows between 1 preceding and current row) sample5,--当前行和前面一行做聚合
      	sum(cost) over(partition by name order by orderdate rows between 1 preceding and 1 following) sample6,--前一行、当前行、后一行做聚合
      	sum(cost) over(partition by name order by orderdate rows between 1 preceding and unbounded following) sample7 --前一行到最后一行聚合
      from
      	business;
      
    4. 需求四:查看顾客上次的购买时间

      select
      	name,
      	orderdate,
      	cost,
      	lag(orderdate, 1, '1900-01-01') over(partition by name order by	orderdate) time1--求上次购买时间,默认值:1900-01-01
      from
      	business;
      
    5. 需求五:查询前20%时间的订单信息

      select
      	*
      from
      	(
      	select
      		name, orderdate, cost, ntile(5) over(
      		order by orderdate) sorted
      	from
      		business ) t
      where
      	t.sorted = 1;
      

3.6 Rank

  1. 函数说明

    RANK(): 排序相同时会重复,总数不会变

    DENSE_RANK(): 排序相同时会重复,总数会减少

    ROW_NUMBER():会根据顺序计算

  2. 数据准备

    表 数据准备
name subject score
孙悟空 语文 87
孙悟空 数学 95
孙悟空 英语 68
大海 语文 94
大海 数学 56
大海 英语 84
宋宋 语文 64
宋宋 数学 86
宋宋 英语 84
婷婷 语文 65
婷婷 数学 85
婷婷 英语 78
  1. 需求

    计算每门学科成绩排名。

  2. 创建本地 score.txt,导入数据

    孙悟空	语文	87
    孙悟空	数学	95
    孙悟空	英语	68
    大海	语文	94
    大海	数学	56
    大海	英语	84
    宋宋	语文	64
    宋宋	数学	86
    宋宋	英语	84
    婷婷	语文	65
    婷婷	数学	85
    婷婷	英语	78
    
  3. 创建hive表并导入数据

    create table score(
    name string,
    subject string, 
    score int) 
    row format delimited fields terminated by "\t";
    load data local inpath '/opt/module/datas/score.txt' into table score;
    
  4. 根据需求查询数据

    select
    	name,
    	subject,
    	score,
    	rank() over(partition by subject order by score desc) rp,--排序相同时会重复,总数不会变
    	dense_rank() over(partition by subject order by score desc) drp,--排序相同时会重复,总数会减少
    	row_number() over(partition by subject order by	score desc) rmp	--会根据顺序计算
    	from score;
    

4. 自定义函数

  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 explore()

  4. 官方文档地址

    https://cwiki.apache.org/confluence/display/Hive/HivePlugins

  5. 编程步骤:

    1. 继承org.apache.hadoop.hive.ql.UDF

    2. 需要实现evaluate函数;evaluate函数支持重载;

    3. 在hive的命令行窗口创建函数

      1. 添加 jar

        add jar linux_jar_path
        
      2. 创建 function

        create [temporary] function [dbname.]function_name AS class_name;
        
    4. 在 hive 的命令行窗口删除函数

      drop [temporary] function [if exists] [dbname.]function_name;
      
  6. 注意事项

    UDF必须要有返回类型,可以返回null,但是返回类型不能为void;

5. 自定义 UDF 函数

  1. 创建一个 maven 工程

  2. 导入依赖

    <dependencies>
        <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>1.2.1</version>
        </dependency>
    </dependencies>
    
  3. 创建一个类

    package com.big.data.hive.udf;
    
    import org.apache.commons.lang.StringUtils;
    import org.apache.hadoop.hive.ql.exec.UDF;
    import org.json.JSONObject;
    
    /**
     * 自定义 UDF 函数
     */
    public class BaseFieldUDF extends UDF {
          
          
    
        public static String evaluate(String line, String jsonKeysString) {
          
          
    
            // 0 准备一个sb
            StringBuilder sb = new StringBuilder();
    
            // 1 切割jsonkeys  mid uid vc vn l sr os ar md
            String[] jsonkeys = jsonKeysString.split(",");
    
            // 2 处理line   服务器时间 | json
            String[] logContents = line.split("\\|");
    
            // 3 合法性校验
            if (logContents.length != 2 || StringUtils.isBlank(logContents[1])) {
          
          
                return "";
            }
    
            try {
          
          
                JSONObject jsonObject = new JSONObject(logContents[1]);
    
                // 获取cm里面的对象
                JSONObject base = jsonObject.getJSONObject("cm");
    
                for (int i = 0; i < jsonkeys.length; i++) {
          
          
                    String filedName = jsonkeys[i].trim();
                    if (base.has(filedName)) {
          
          
                        sb.append(base.getString(filedName)).append("\t");
                    } else {
          
          
                        sb.append("\t");
                    }
                }
                sb.append(jsonObject.getString("et")).append("\t");
                sb.append(logContents[0]).append("\t");
            }catch (Exception e) {
          
          
                e.printStackTrace();
            }
    
            return sb.toString();
        }
    
        public static void main(String[] args) throws Exception{
          
          
    
            String line = "1541217850324|{\"cm\":{\"mid\":\"m7856\",\"uid\":\"u8739\",\"ln\":\"-74.8\",\"sv\":\"V2.2.2\",\"os\":\"8.1.3\",\"g\":\"[email protected]\",\"nw\":\"3G\",\"l\":\"es\",\"vc\":\"6\",\"hw\":\"640*960\",\"ar\":\"MX\",\"t\":\"1541204134250\",\"la\":\"-31.7\",\"md\":\"huawei-17\",\"vn\":\"1.1.2\",\"sr\":\"O\",\"ba\":\"Huawei\"},\"ap\":\"weather\",\"et\":[{\"ett\":\"1541146624055\",\"en\":\"display\",\"kv\":{\"goodsid\":\"n4195\",\"copyright\":\"ESPN\",\"content_provider\":\"CNN\",\"extend2\":\"5\",\"action\":\"2\",\"extend1\":\"2\",\"place\":\"3\",\"showtype\":\"2\",\"category\":\"72\",\"newstype\":\"5\"}},{\"ett\":\"1541213331817\",\"en\":\"loading\",\"kv\":{\"extend2\":\"\",\"loading_time\":\"15\",\"action\":\"3\",\"extend1\":\"\",\"type1\":\"\",\"type\":\"3\",\"loading_way\":\"1\"}},{\"ett\":\"1541126195645\",\"en\":\"ad\",\"kv\":{\"entry\":\"3\",\"show_style\":\"0\",\"action\":\"2\",\"detail\":\"325\",\"source\":\"4\",\"behavior\":\"2\",\"content\":\"1\",\"newstype\":\"5\"}},{\"ett\":\"1541202678812\",\"en\":\"notification\",\"kv\":{\"ap_time\":\"1541184614380\",\"action\":\"3\",\"type\":\"4\",\"content\":\"\"}},{\"ett\":\"1541194686688\",\"en\":\"active_background\",\"kv\":{\"active_source\":\"3\"}}]}";
    
            String x = BaseFieldUDF.evaluate(line, "mid,uid,vc,vn,l,sr,os,ar,md,ba,sv,g,hw,nw,ln,la,t");
            
        }
    }
    
    
    1. 打成jar包上传到服务器/opt/module/jars/hive-udf.jar

    2. 将jar包添加到hive的classpath

      hive (default)> add jar /opt/module/datas/hive-udf.jar;
      
    3. 创建临时函数与 class关联

      hive (default)> create temporary function base_analizer as "com.big.data.hive.udf.BaseFieldUDF";
      
    4. 即可在hql中使用自定义的函数base_analizer

      hive (default)> select
      split(base_analizer(line,'mid,uid,vc,vn,l,sr,os,ar,md,ba,sv,g,hw,t,nw,ln,la'),'\t')[0]   as mid_id from ods_event_log;
      

6. 自定义 UDTF 函数

6.1 自定义 UDTF 步骤

  1. 创建一个类,继承 org.apache.hadoop.hive.ql.udf.generic.GenericUDTF
  2. 重写 initialize方法,指定输出参数的名称和参数类型。
  3. 重写 process,输入一条记录,输出若干个结果。
  4. 重写 close方法。

6.2 具体实现

  1. 创建一个类

    package com.big.data.hive.udtf;
    
    import org.apache.commons.lang.StringUtils;
    import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
    import org.apache.hadoop.hive.ql.metadata.HiveException;
    import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
    import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
    import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
    import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
    import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
    import org.json.JSONArray;
    import org.json.JSONException;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 自定义 UDTF 函数
     */
    public class EventJsonUDTF extends GenericUDTF {
          
          
    
    
        /**
         * 初始化
         * 指定输出参数的名称和参数类型
         * @param argOIs
         * @return
         * @throws UDFArgumentException
         */
        @Override
        public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
          
          
    
            ArrayList<String> fieldNameList = new ArrayList<>();
            ArrayList<ObjectInspector> fieldOIList = new ArrayList<>();
            fieldNameList.add("event_name");
            fieldOIList.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
            fieldNameList.add("event_json");
            fieldOIList.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
    
            return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNameList,fieldOIList);
    
        }
    
        /**
         * 输入一条记录,输出若干结果
         * @param objects
         * @throws HiveException
         */
        @Override
        public void process(Object[] objects) throws HiveException {
          
          
    
            if (objects == null || objects.length < 1) {
          
          
                return;
            }
            // 获取传入的 et
            Object object = objects[0];
            if (object == null) {
          
          
                return;
            }
            String input = object.toString();
    
            if (StringUtils.isEmpty(input)) {
          
          
                return;
            }
    
            JSONArray jsonArray = null;
            try {
          
          
                jsonArray = new JSONArray(input);
            } catch (JSONException e) {
          
          
                e.printStackTrace();
            }
            if (jsonArray == null || jsonArray.length() <= 0) {
          
          
                return;
            }
    
            for (int i = 0; i < jsonArray.length(); i++) {
          
          
    
                try {
          
          
                    String[] result = new String[2];
                    // 取出事件名称
                    result[0] = jsonArray.getJSONObject(i).getString("en");
                    // 取出整个事件
                    result[1] = jsonArray.getString(i);
                    forward(result);
                }catch (Exception e) {
          
          
                    e.printStackTrace();
                    continue;
                }
            }
        }
    
        @Override
        public void close() throws HiveException {
          
          
    
        }
    }
    
    1. 打成jar包上传到服务器/opt/module/jars/hive-udtf.jar

    2. 将jar包添加到hive的classpath

      hive (default)> add jar /opt/module/datas/hive-udtf.jar;
      
    3. 创建临时函数与 class关联

      hive (default)> create temporary function flat_analizer as 'com.big.data.hive.udtf.EventJsonUDTF';
      
    4. 即可在hql中使用自定义的函数flat_analizer

    select
    	event_name,
    	event_json
    from
    	(
    	select
    		split(base_analizer(line, 'mid,uid,vc,vn,l,sr,os,ar,md,ba,sv,g,hw,t,nw,ln,la'), '\t')[17] as ops
    	from
    		ods_event_log ) t lateral view flat_analizer(t.ops) tem_k as event_name,
    	event_json;
    

猜你喜欢

转载自blog.csdn.net/dwjf321/article/details/112726756