实训笔记8.22

8.22笔记

一、Hive的HQL语法重点问题

1.1 DDL

1.1.1 Hive中数据表的分类问题

Hive中数据表的分类只是用来告诉我们Hive底层在HDFS上存储的文件的规则和规范

1.1.2 特殊的数据类型

array、map、struct

Hive数据表可以指定底层的存储格式的分隔符问题row format

1.2 DML

Hive中默认不支持批量的删除和更新操作

  1. 增加数据
    1. 装载数据
    2. 追加和覆盖
  2. 删除数据(只能删除所有数据或者某个分区的数据)–truncate
  3. 数据导入和导出问题

1.3 DQL

1.3.1 查询语法和MySQL大部分都是一致的
  1. 排序问题

    1. 全局排序:DQL查询语法转换的MR底层只有一个reduce任务
      order by

    2. 局部排序:DQL查询语法转换的MR底层可以有多个reduce任务,每个reduce的输出有序,整体没有顺序sort by

      如果要使用sort by进行局部排序,那么需要设置Hive底层的转换的MR程序的reduce任务数大于1

      set mapreduce.job.reduces=num>1

      注意:如果我们只是使用了sort by进行排序,并且reduce的任务数大于1,那么mr程序计算的时候底层会对数据进行分区,分区数就是reduce的任务数,默认情况下,如果只使用了sort by,那么分区机制我们是无法控制

      如果我们在局部排序的时候还想控制每个分区的数据,可以在sort by之前增加上一个distribute By 字段,Distribute By和sort by结合使用的,是用于负责控制分区规则的。分区规则是根据我们指定的分区字段的hash值和分区数取余数。

      如果Distribute By和sort by的字段一样,我们可以使用cluster by替代上述两个操作

  2. 连接查询

    Hive支持了全外连接full join

1.4 讲了三个数据库的可视化工具

1.4.1 navicat

界面特别好看的,收费的

一般只能连接RDBMS关系型数据库,连接底层不是基于JDBC

1.4.2 dbeaver
1.4.3 chat2db

增加了AI大模型

1.4.2~1.4.3:都是免费的,底层都是基于JDBC连接数据库
因此这两个软件不仅可以连接常见的RDBMS,还可以连接大数据环境:Hive、Spark等等

二、Hive中重点问题:Hive函数的使用

Hive中提供了很多的自带函数,自带函数有大部分都是为了统计分析设计的。Hive中的函数大部分都是一个Java类

2.1 函数分为两种

UDF:一对一函数
UDTF:一对多函数
UDAF:多对一函数

2.1.1 内置函数
  1. 如何查看系统自带的内置函数

show functions;

desc function 函数名;

desc function extended 函数名;

  1. Hive中常见的一些内置函数的用法

    1. 数学函数:UDF

      函数名说明
      abs(x)返回x的绝对值
      ceil(x)向上取整,返回比x大的正整数中最小的那一个
      floor(x)向下取整
      mod(a,b)a%b
      pow(a,b)a^b
      round(x,[n])四舍五入 如果不传递n 代表小数点不保留,如果n>=1 代表小数点后保留n位
      sqrt(x)根号x
    2. 字符串函数

      函数名说明
      concat直接拼接,拼接需要传递多个参数,会把多个参数拼接起来,如果有一个参数为null值,那么结果直接为null
      concat_ws可以拼接的分隔符,传递的第一个参数是一个分隔符,如果拼接了null值,null值不计算
      lpad |rpad(str,x,pad)在str左/右边以指定的pad字符填充字符串到指定的x长度
      ltrim|rtrim|trim(str)去除空格
      length(x)返回字符串的长度
      replace(str,str1,replacestr)将字符串中指定字符串替换成为另外一个字符串
      everse(str)字符串反转
      split(str,x):array字符串切割
      substr|substring(str,n,[num]),截取字符串
    3. 日期

      函数名说明
      current_date()返回当前的日期 年月日
      current_timestamp()返回当前时间
      date_format(date,“格式”)格式化时间的
      datediff(date,date1)返回这两个时间的差值(天)
    4. 条件判断函数

      函数名
      if
      case when then [when then] … else end
    5. 特殊函数

      1. 和数组、集合操作有关的函数

        函数内部需要传递一个数组,或者返回值是一个数组类型的函数

        函数名说明
        split(str,spea):array
        collect_set(列名):array将一列中的所有行的数据封装为一个数组 列转行 不允许重复
        将一列中的所有行的数据封装为一个数组 列转行 允许重复
        array(ele…):array
        map(key,value,key,value,key,value…):map
        concat_ws(spe,array(string)):String将一个数组中的所有字符串以指定的分隔符拼接得到一个全新的字符串
        explode(array,map集合)多行多列的数据 炸裂函数 行转列的函数
        如果传递的是array,那么结果是一列多行
        如果传递的是map集合,那么结果就是两列多行

        collect_set(列名):array、collect_list(列名):array

        列转行函数
        将一列的多行数据转换成为一行数据
        UTAF

      2. 和字符串有关的特殊函数:(字符串必须得是URL)

        1. URL的概念

          URL是叫做统一资源定位符,是用来表示互联网或者主机上的唯一的一个资源的
          URL整体上主要有如下几部分组成的:
          协议:http/https、ftp、file、ssh
          host:主机名、域名、ip地址
          port:端口号
          path:资源路径
          queryParam:参数 ?key=value&key=value…

          例子:

          http://192.168.35.101:9870/index.html?name=zs&age=20

          https://www.baidu.com/search/a?key=valueURL

          中,如果没有写端口号,那么都是有默认端口,http:80 https:443 ssh:22

        2. parse_url(urlstr,"特殊字符"):string一次只能提取URL的一个成分

        3. parse_url_tuple(urlstr,"特殊字符"...):每一个成分当作一列单独展示,函数可以将一个数据转换成为一行多列的数据函数多了一个特殊字符:QUERY:key

          parse_url_tuple(urlstr,"特殊字符"...) as (列名...)

          2、3:

          hive提供用来专门用来解析URL的函数:从URL中提取URL组成成分
          特殊字符代表的是URL的组成成分,特殊字符有如下几种:
          HOST:提取URL中的主机名、IP地址
          PATH,:提取URL中资源路径
          QUERY, 提取URL中的所有请求参数
          PROTOCOL, 提起URL中的请求协议
          AUTHORITY,
          FILE,
          USERINFO,
          REF,

      3. 侧视图

        1. 侧视图Lateral View专门用来和UDTF函数结合使用,用来生成一个虚拟表格,然后这个虚拟表格一行数据会生成一个,虚拟表格是动态的,一行数据会生成一个虚拟表格,生成的虚拟表格和当前行做一个笛卡尔乘积,得到一些我们普通SQL无法实现的功能
        2. 侧视图使用场景:一个表格中,某一行的某一列是一个多字段组成的数据,我们想把多字段的列拆分开和当前行结合得到一个多行的结果。
      4. 开窗函数 overselect子语句中

        1. 开窗函数指的是在查询表数据时,将表按照指定的规则拆分成为多个虚拟窗口(并没有真实的拆分、类似于分组),然后可以在窗口中得到一些只有分组之后才能得到一些信息,然后将信息和原始数据结合起来,实现在同一个查询中既可以得到基础字段,还可以得到聚合字段。既需要普通字段还需要一些聚合信息的时候,开窗函数就是最完美的选择。

        2. 语法:

          函数(参数) over(partition by 列名 order by 字段) as 别名-列名

        3. 可以和窗口函数结合使用的主要有三种类型的函数

          1. first_value(col)|last_value(col) over(partition by 列名 order by 字段) as 别名-列名

          2. 聚合函数

            sum/avg/count/max/min over(partition by 列名) as 列别名

          3. 排名函数

            row_number()/rank()/dense_rank() over(partition by 列名 order by 字段) as 列别名

            排名函数的作用就是对数据开窗之后,查询到某一行数据之后,看一下这行数据在所属窗口的排名-位置,然后根据位置打上一个序号,序号从1开始

            函数名说明
            row_number()序号是从1开始依次递增,如果两行数据排名一致,也会依次编号
            rank()序号是从1开始依次递增,如果两行数据排名一致,两行编号一样的 跳排名
            dense_rank()序号是从1开始依次递增,如果两行数据排名一致,也会依次编号,不会跳排名
          4. 使用场景:求不同组中排名topN的数据信息

2.2 用户自定义函数

用户自定义函数就是我们觉得hive内置函数不满足我们的需求,我们可以自定义函数实现我们想要的功能

2.2.1 Hive自定义函数的步骤

(Hive底层也都是Java,自定义函数也是编写Java代码的)

  1. 创建一个Java项目

  2. 引入编程依赖

    1. 创建lib目录,自己找jar包放到lib目录下,然后lib目录add as library hive的安装目录的lib目录下
    2. 使用maven然后根据gav坐标引入依赖
  3. 编写对应的函数类:UDF、UDTF、UDAF
    大部分自定义都是UDF和UDTF函数

  4. 将编写好的Java代码打成jar包

  5. 将jar包上传到HDFS上

  6. 通过create function …从jar包以全限定类名的方式创建函数

  7. 注意:自定义的函数创建的时候有两种创建方式
    create [temporary] function function_name as “全限定类名” using jar “jar包在hdfs上的路径”

    1. 临时函数:只对本次会话有效
      只可以通过show functions查看,元数据库不会记录
    2. 永久函数:永久生效
      无法通过show functions查看,但是可以通过Hive的元数据库的FUNS表中查看
2.2.2 自定义UDF函数
2.2.3 自定义UDTF函数
2.2.4 删除自定义函数

drop function 函数名

【注意】用户自定义函数有一个特别重要的问题,自定义函数和数据库绑定的。只能在创建函数的数据库使用函数。如果要在其他数据库下使用,需要在其他数据库下把函数重新创建一遍即可。

三、相关代码

-- 查看hive自带的所有函数
show functions;
-- 查看某个函数的用法
desc function abs;
-- 查看某个函数的详细用法
desc function extended parse_url;

select sqrt(8);

create table demo(
	name string
);
insert into demo values("zs"),("ls"),("ww");
select * from demo;
select concat_ws("-",collect_set(name)) from demo;



select concat_ws("-",name) from demo;
select concat_ws("-","zs","ls",null);

select rpad("zs",10,"-");

select ltrim("    z   s     ");
select rtrim("    z   s     ");
select trim("    z    s     ");

select replace("2022-10-11","-","/");
select reverse("zs");
select split("zs-ls-ww","-");

select substring("zs is a good boy",9);

select current_timestamp();
select date_format("2022-10-11 20:00:00","HH")
select datediff(current_date(),"2000-10-11");

select if(1>2,"zs","ls");
select 
   CASE 10
   when 10 then "zs"
   when 20 then "ls"
   else "ww"
   END

select 
   CASE 
   	when 1>2 then "zs"
   	when 1<2 then "ls"
   	else "ww"
   END
   
select explode(array(1,2,3,4,5));
select map("name","zs","age","20","sex","man");
select explode(map("name","zs","age","20","sex","man"));

select parse_url("http://www.baidu.com:80/search/a?name=zs&age=30","QUERY");
select parse_url_tuple("http://www.baidu.com:80/search/a?name=zs&age=30","QUERY","PATH","HOST","QUERY:sex") 
as (query,path,host,sex);

-- 侧视图的使用
create table test(
   name string,
   age int,
   hobby array<string>
);

insert into test values("zs",20,array("play","study")),("ls",30,array("sleep","study"));
select * from test;

select name,age,hobby,temp.hb from test
lateral view explode(hobby) temp as hb;

-- 开窗函数的使用
create table student(
	student_name string,
	student_age int,
	student_sex string
);

insert into student values("zs",20,"man"),("ls",20,"woman"),("ww",20,"man"),("ml",20,"woman"),("zsf",20,"man");

select * from student;

-- 查询不同性别的总人数
select student_sex,count(1) from student group by student_sex;
-- 查询表中所有的学生信息,并且每个学生信息后面需要跟上这个学生所属性别组的总人数
select 
   student_name,
   student_age,
   student_sex,
   row_number() over(partition by student_sex order by student_name desc) as sex_count
from student;

-- 排名函数的使用场景
create table employees(
   employees_id int,
   employees_name string,
   employees_dept int,
   employees_salary double
);

insert into employees values(1,"zs",1,2000.0),
							(2,"ls",1,1800.0),
							(3,"ww",1,1700.0),
							(4,"ml",1,2000.0),
							(5,"zsf",1,1900.0),
							(6,"zwj",2,3000.0),
							(7,"qf",2,2500.0),
							(8,"cl",2,2500.0),
							(9,"jmsw",2,2000.0);

select * from employees;
-- 获取每个部门薪资排名前二的员工信息
-- 部门分组 薪资降序排序  排名窗口函数  给每一行数据打上一个序号
select * from(
	select 
	   *,
	   DENSE_RANK() over(partition by employees_dept order by employees_salary desc) as salary_rank
	from employees
) as b
where salary_rank <=2;
package com.sxuek.udf;

import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
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;

/**
 * 类就是从一个字符串中找大写字符个数的函数
 * UDF
 */
public class FindUpperCount extends GenericUDF {
    /**
     * 初始化方法,方法是用来判断函数参数的
     *   指定函数参数的个数以及函数参数的类型
     * @param objectInspectors   函数参数的类型和个数的一个数组
     * @return  方法的返回值代表的是函数执行完成之后的返回值类型
     * @throws UDFArgumentException
     */
    @Override
    public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException {
        /**
         * 1、判断参数的类型和个数是否满足需求
         */
        //数组的长度就是函数参数的个数
        int length = objectInspectors.length;
        if (length != 1){
            throw new UDFArgumentException("function only need one param");
        }else{
            //ObjectInspector是一个Hive数据类型的顶尖父类 参数的类型
            ObjectInspector objectInspector = objectInspectors[0];
            //PrimitiveObjectInspectorFactory是Hive中所有基础数据类型的工厂类
            //返回函数的执行完成之后输出的结果类型  整数类型
            return PrimitiveObjectInspectorFactory.javaIntObjectInspector;
        }
    }

    /**
     * 方法就是函数实现的核心逻辑方法
     * @param deferredObjects   函数传递的参数
     * @return 返回值就是函数执行完成之后的返回结果 返回结果必须和initialize的返回值类型保持一致
     * @throws HiveException
     */
    @Override
    public Object evaluate(DeferredObject[] deferredObjects) throws HiveException {
        //获取函数传递的那一个参数
        DeferredObject deferredObject = deferredObjects[0];
        //get方法是获取封装的参数值
        Object o = deferredObject.get();
        String str = o.toString();
        int num = 0;
        for (char c : str.toCharArray()) {
            if (c >= 65 && c <= 90){
                num++;
            }
        }
        return num;
    }

    /**
     * HQL的解析SQL的输出----没有用处
     * @param strings
     * @return
     */
    @Override
    public String getDisplayString(String[] strings) {
        return "";
    }
}

package com.sxuek.udtf;

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.*;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;

import java.util.ArrayList;
import java.util.List;

/**
 *  输入参数有两个:
 *     字符串
 *     分隔符
 *  输出结果 一列多行的结果
 *     word
 *     zs
 *     ls
 */
public class SplitPlus extends GenericUDTF {
    /**
     * 作用:
     *   1、校验输入的参数
     *   2、返回UDTF函数返回的列的个数、名字、类型
     * @param argOIs  当作一个数组来看,里面多个参数组成的
     * @return
     * @throws UDFArgumentException
     */
    @Override
    public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
        List<? extends StructField> allStructFieldRefs = argOIs.getAllStructFieldRefs();
        if (allStructFieldRefs.size() != 2){
            throw new UDFArgumentException("function need two params");
        }else{
            /**
             * 返回一列多行 UDTF函数也可以返回多行多列
             */
            //返回的列的名字 是一个集合 集合有几项  代表UDTF函数返回几列
            List<String> columnNames = new ArrayList<>();
            columnNames.add("word");

            //返回的列的类型 集合的个数必须和columnNames集合的个数保持一致
            List<ObjectInspector> columnTypes = new ArrayList<>();
            columnTypes.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);

            //构建StandardStructObjectInspector,需要两个List集合 List<String> List<ObjectInspector>
            StandardStructObjectInspector standardStructObjectInspector = ObjectInspectorFactory.getStandardStructObjectInspector(columnNames,columnTypes);
            return standardStructObjectInspector;
        }
    }

    /**
     * UDTF函数执行的核心逻辑
     *   结果的输出需要借助forward方法
     * @param objects 函数的输入参数
     * @throws HiveException
     */
    @Override
    public void process(Object[] objects) throws HiveException {
        String str = objects[0].toString();
        String split = objects[1].toString();
        String[] array = str.split(split);
        for (String s : array) {
            //一行输出需要输出一次  如果输出一行数据 那么只需要调用一次forward方法即可
            /**
             * 如果一行数据有多列,可以先创建一个List集合,List<Object> 集合中把一行的多列值全部加加进来
             */
            forward(s);
        }
    }

    /**
     * close用于关闭一些外部资源
     * @throws HiveException
     */
    @Override
    public void close() throws HiveException {

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cai-4

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值