21-Hive运算符&函数

   日期:2024-12-27    作者:0xp5s 移动:http://oml01z.riyuangf.com/mobile/quote/61038.html

整体上,Hive 支持的运算符可以分为三大类:关系运算、算术运算、逻辑运算。

官方参考文档:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+UDF

也可以使用下述方式查看运算符的使用方式:


从 Hive 0.13.0 开始,select 查询语句 FROM 关键字是可选的(例如 SELECT 1+1),因此可以使用这种方式来练习测试内置的运算符、函数的功能。除此之外,还可以通过创建一张虚表 dual 来满足于测试需求。



关系运算符是二元运算符,执行的是两个操作数的比较运算。每个关系运算符都返回 boolean 类型结果(TRUE/FALSE)。

示例:



算术运算符操作数必须是数值类型。分为一元运算符和二元运算符:一元运算符,只有一个操作数;二元运算符有两个操作数,运算符在两个操作数之间。

  • 加法操作:+
  • 减法操作:-
  • 乘法操作:*
  • 除法操作:/
  • 取整操作:div
  • 取余操作:%
  • 位与操作:&
  • 位或操作:|
  • 位异或操作:^
  • 位取反操作: ~

示例:



  • 与操作:A AND B
  • 或操作:A OR B
  • 非操作:NOT A 、!A
  • 在:A IN (val1, val2, ...)
  • 不在:A NOT IN (val1, val2, ...)
  • 逻辑是否存在:[NOT] EXISTS (subquery)

示例:



Hive 内建了不少函数,用于满足用户不同使用需求,提高 SQL 编写效率:

  • 使用 查看当下可用的所有函数;
  • 通过 来查看函数的使用方式。

Hive 的函数分为两大类:内置函数(Built-in Functions)、用户定义函数 UDF(User-Defined Functions)

  • 内置函数可分为:数值类型函数、日期类型函数、字符串类型函数、集合函数、条件函数等;
  • 用户定义函数根据输入输出的行数可分为 3 类:UDF、UDAF、UDTF。

UDF 分类标准 — 根据函数输入输出的行数

  • UDF(User-Defined-Function)普通函数,一进一出
  • UDAF(User-Defined Aggregation Function)聚合函数,多进一出
  • UDTF(User-Defined Table-Generating Functions)表生成函数,一进多出

UDF 分类标准 · 扩大化

UDF 分类标准本来针对的是用户自己编写开发实现的函数。UDF 分类标准可以扩大到 Hive 的所有函数中,包括「内置函数」和「用户自定义函数」。

因为不管是什么类型的函数,一定满足于输入输出的要求,那么从输入几行和输出几行上来划分没有任何问题。所以千万不要被 UD(User-Defined)这两个字母所迷惑,造成视野的狭隘!

比如 Hive 官方文档中,针对聚合函数的标准就是内置的 UDAF 类型。

内置函数(Build-in)指的是 Hive 开发实现好,直接可以使用的函数,也叫做内建函数。

内置函数根据应用归类整体可以分为 8 大类,我们将对其中重要的、使用频率高的函数使用进行详细讲解。

示例:




示例:




示例:




示例:




示例:



主要用于显式的数据类型转换:



主要完成对数据脱敏转换功能,屏蔽原始数据,主要如下:

示例:




示例:



虽然说 Hive 内置了很多函数,但是不见得一定可以满足于用户各种各样的分析需求场景。为了解决这个问题,Hive 推出来用户自定义函数功能,让用户实现自己希望实现的功能函数。

用户自定义函数简称 UDF,源自于英文 User-Defined Function。自定义函数总共有 3 类,是根据函数输入输出的行数来区分的,分别是:

(1)UDF 普通函数

UDF 函数通常被叫做普通函数,最大的特点是一进一出,也就是输入一行输出一行。比如 round 取整函数,接收一行数据,输出的还是一行数据。

(2)UDAF 聚合函数

UDAF 函数通常把它叫做聚合函数,A 所代表的单词就是 Aggregation 聚合的意思。最大的特点是多进一出,也就是输入多行输出一行。比如 count、sum 这样的函数。


(3)UDTF 表生成函数

UDTF 函数通常把它叫做表生成函数,T 所代表的单词是 Table-Generating 表生成的意思。最大的特点是一进多出,也就是输入一行输出多行。之所以叫做表生成函数,原因在于这类型的函数作用返回的结果类似于表(多行数据嘛),同时 UDTF 函数也是我们接触比较少的函数。

在企业中处理数据的时候,对于敏感数据往往需要进行脱敏处理。比如手机号。我们常见的处理方式是将手机号中间 4 位进行 处理。

Hive 中没有这样的函数可以直接实现功能,虽然可以通过各种函数的嵌套调用最终也能实现,但是效率不高,现要求自定义开发实现 Hive 函数,满足上述需求。

  1. 能够对输入数据进行非空判断、手机号位数判断;
  2. 能够实现校验手机号格式,把满足规则的进行处理;
  3. 对于不符合手机号规则的数据直接返回,不处理。
  1. 写一个 Java 类,继承 UDF,并重载 evaluate 方法,在方法中实现函数的业务逻辑;
  2. 重载意味着可以在一个 Java 类中实现多个函数功能;
  3. 程序打成 jar 包,上传 HS2 服务器本地或者 HDFS;
  4. 客户端命令行中添加 jar 包到 Hive 的 classpath:
  5. 注册成为临时函数(给 UDF 命名):
  6. HQL 中使用函数。

(1)新建 Maven 项目 · pom 依赖


(2)编写业务代码 · UDF 类


(3)IDEA 中使用集成的 Maven 插件进行打包,这里会把依赖一起打入 jar 包;

(4)把 jar 包上传到 Hiveserver2 服务运行所在机器的 Linux 系统,上传 HDFS 文件系统也可以,后续路径指定清楚即可;

(5)在客户端中使用命令把 jar 包添加至 classpath;


(6)注册临时函数(通俗来说就是给用户编写的函数起个名字);



(7)注册成功,使用 UDF。

(8)删除临时函数


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

上面案例的实现方式是「临时注册」,还可以创建永久函数。

  • 永久函数跟会话没有关系,创建函数的会话断了以后,其他会话也可以使用;
  • 永久函数创建的时候,在函数名之前需要自己加上库名,如果不指定库名的话,会默认把当前库的库名给加上;
  • 永久函数使用的时候,需要在指定的库里面操作,或者在其他库里面使用的话加上“库名.”。

操作如下:

(1)创建永久函数

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


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


(3)删除永久函数





对于 UDTF 表生成函数,很多人难以理解什么叫做输入一行,输出多行。

为什么叫做表生成?能够产生表吗?

下面就来看下 Hive 内置的一个非常著名的 UDTF 函数,名字叫做 explode 函数,中文戏称之为“爆炸函数”,可以炸开数据。

explode 函数接收 map 或 array 类型的数据作为参数,然后把参数中的每个元素炸开变成一行数据。一个元素一行。这样的效果正好满足于输入一行输出多行。

explode 函数在关系型数据库中本身是不该出现的。因为他的出现本身就是在操作不满足第一范式的数据(每个属性都不可再分)。本身已经违背了数据库的设计原理,但是在面向分析的数据库或者数据仓库中,这些规范可以发生改变。

一般情况下,explode 函数直接使用即可,也可以根据需要结合 Lateral View 侧视图使用。

  • 将 array 列表里的每个元素生成一行;
  • 将 map 里的每一对元素作为一行,其中 key 为一列,value 为一列。
  • 将 explode 的每行结果还附带索引;
  • 结构体列表中每个结构体生成一行(属性名对应成列名)

有一份数据 The_NBA_Championship.txt,关于部分年份的 NBA 总冠军球队名单:

第 1 个字段表示的是球队名称,第 2 个字段是获取总冠军的年份,字段之间以 分割;获取总冠军年份之间以 进行分割。

使用 Hive 建表映射数据并对数据拆分,要求拆分之后数据如下所示并根据年份倒序排序:

(1)建表


(2)加载数据文件到表中


(3)验证数据是否加载成功

(4)使用 explode 函数对 champion_year 进行拆分

(5)explode 使用限制

在 select 条件中,如果只有 explode 函数表达式,程序执行是没有任何问题的。但是如果在 select 条件中,除了 explode 外还包含其他字段,就会报错:org.apache.hadoop.hive.ql.parse.SemanticException:UDTF's are not supported outside the SELECT clause, nor nested in expressions。

如何理解这个错误呢?为什么在 select 的时候,explode 的旁边不支持其他字段的同时出现?

【原因】explode 函数属于 UDTF 表生成函数,explode 执行返回的结果可以理解为一张虚拟的表,其数据来源于源表。在 select 中只查询源表数据没有问题,只查询 explode 生成的虚拟表数据也没问题。但不能在只查询源表的时候,想既返回源表字段又返回 explode 生成的虚拟表字段。通俗点讲就是,有两张表,不能只查一张表却又想返回分别属于两张表的字段。

【解决】从 SQL 层面上来说上述问题的解决方案是:对两张表进行 join 关联查询。Hive 专门提供了语法 Lateral View 侧视图,专门用于搭配 explode 这样的 UDTF 函数,以满足上述需要。


(6)explode + lateral view

Lateral View 是一种特殊的语法,主要用于搭配 UDTF 类型功能的函数一起使用,用于解决 UDTF 函数的一些查询限制的问题。

侧视图的原理是将 UDTF 的结果构建成一个类似于视图的表,然后将原表中的每一行和 UDTF 函数输出的每一行进行连接,生成一张新的虚拟表。这样就避免了 UDTF 的使用限制问题。

使用 Lateral View 时也可以对 UDTF 产生的记录设置字段名称,产生的字段可以用于 group by、order by 、limit 等语句中,不需要再单独嵌套一层子查询。一般只要使用 UDTF,就会固定搭配 Lateral View 使用。

官方链接:https://cwiki.apache.org/confluence/display/Hive/LanguageManual+LateralView

针对上述 NBA 冠军球队年份排名案例,使用 explode 函数 + Lateral View 侧视图,可以完美解决:



聚合函数的功能是:对一组值执行计算并返回单一的值

聚合函数是典型的输入多行输出一行,使用 Hive 的分类标准,属于 UDAF 类型函数。

通常搭配 Group By 语法一起使用,分组后进行聚合操作。

HQL 提供了几种内置的 UDAF 聚合函数,例如 max/min/avg ... 这些我们把它称之为基础的聚合函数。

通常情况下聚合函数会与 GROUP BY 子句一起使用。如果未指定 GROUP BY 子句,默认情况下,它会汇总所有行数据。

(1)测试数据准备



(2)聚合函数·示例



增强聚合包括 grouping_sets、cube、rollup 这几个函数。主要适用于 OLAP 多维数据分析模式中,多维分析中的维指的分析问题时看待问题的维度、角度。

下面通过案例更好的理解函数的功能含义(字段:月份、天、用户标识 cookieid)。

(1)GROUPING SETS

GROUPING SETS 是一种将多个 GROUP BY 逻辑写在一个 sql 语句中的便利写法。等价于将不同维度的 GROUP BY 结果集进行 UNION ALL。

GROUPING__ID 表示结果属于哪一个分组集合。


查询结果:

(2)CUBE

CUBE 表示根据 GROUP BY 的维度的所有组合进行聚合。

对于 CUBE 来说,如果有 N 个维度,则所有组合的总个数是:2^N。比如 cube 有 a,b,c 三个维度,则所有组合情况是:(a,b,c),(a,b),(b,c),(a,c),(a),(b),(c),()。


查询结果:

(3)ROLLUP

CUBE 的语法功能是根据 GROUP BY 的维度的所有组合进行聚合,而 ROLLUP 是 CUBE 的子集,以最左侧的维度为主,从该维度进行层级聚合。

比如 ROLLUP 有 a,b,c 三个维度,则所有组合情况是:((a,b,c),(a,b),(a),())。


查询结果:

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

窗口函数(Window Function)也叫做开窗函数、OLAP 函数,其最大特点是:输入值是从 SELECT 语句的结果集中的一行或多行的“窗口”中获取的。

如果函数具有 OVER 子句,则它是窗口函数。如果它缺少 OVER 子句,则它是一个普通的聚合函数。

窗口函数可以简单地解释为类似于聚合函数的计算函数,但是通过 GROUP BY 子句组合的常规聚合会隐藏正在聚合的各个行,最终输出一行,窗口函数聚合后还可以访问当中的各个行,并且可以将这些行中的某些属性添加到结果集中。

窗口函数的语法中主要包括“窗口”和“函数”两部分。其中“窗口”用于定义计算范围,“函数”用于定义计算逻辑。

通过 sum 聚合函数进行「普通常规聚合」和「窗口聚合」来直观感受窗口函数的特点:

(1)窗口范围的定义分为两种类型,一种是基于「行」的,一种是基于「值」的。

(2)基于行/值语法详细说明

between 包括下面这几个选项:

  • preceding:往前
  • following:往后
  • current row:当前行
  • unbounded:边界
  • unbounded preceding:表示从前面的起点
  • unbounded following:表示到后面的终点

(3)分区语法示例

(4)缺省:over() 中的三部分内容均可省略不写

  • 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
  • LAG(col, n, DEFAULT) 用于统计窗口内往上第 n 行值
    • 第 1 个参数为列名
    • 第 2 个参数为往上第 n 行(可选,默认为 1)
    • 第 3 个参数为默认值(当往上第 n 行为 NULL 时候,取默认值,如不指定,则为 NULL)
  • LEAD(col, n, DEFAULT) 用于统计窗口内往下第 n 行值
    • 第 1 个参数为列名
    • 第 2 个参数为往下第 n 行(可选,默认为 1)
    • 第 3 个参数为默认值(当往下第 n 行为 NULL 时候,取默认值,如不指定,则为 NULL)
  • FIRST_VALUE(col, bool) 取分组内排序后,截止到当前行,第一个值;
  • LAST_VALUE(col, bool) 取分组内排序后,截止到当前行,最后一个值;

【注】lag 和 lead 函数不支持自定义窗口设想这么一种情况,自定义窗口范围为下 5 行,然后 lag 里写要上一行的数据,这就冲突了

示例代码:


执行结果:

窗口排序函数用于给每个分组内的数据打上排序的标号。注意窗口排序函数不支持窗口表达式。总共有 4 个函数需要掌握。

(1)ROW_NUMBER:在每个分组中,为每行分配一个从 1 开始的唯一序列号,递增,不考虑重复;

(2)RANK:在每个分组中,为每行分配一个从 1 开始的序列号,考虑重复,挤占后续位置;

(3)DENSE_RANK:在每个分组中,为每行分配一个从 1 开始的序列号,考虑重复,不挤占后续位置;

【注】rank 、dense_rank、row_number 不支持自定义窗口。

举例说明:

上述这三个函数用于分组 TopN 的场景非常适合。

(4)NTILE:将每个分组内的数据分为指定的若干个桶里(分为若干个部分),并且为每一个桶分配一个桶编号。

如果不能平均分配,则优先分配较小编号的桶,并且各个桶中能放的行数最多相差 1。

因为有时会有这样的需求:如果数据排序后分为三部分,业务人员只关心其中的一部分,如何将这中间的三分之一数据拿出来呢?

例1:把每个分组内的数据分为 3 桶

例2:统计每个用户 pv 数最多的前 1/3 天

案例1 · 网站用户页面浏览次数分析

在网站访问中,经常使用 cookie 来标识不同的用户身份,通过 cookie 可以追踪不同用户的页面访问情况,有下面两份数据:

在 Hive 中创建两张表,把数据加载进去用于窗口分析:


从 Hive v2.2.0 开始,支持 DISTINCT 与窗口函数中的聚合函数一起使用。

这里以 sum() 函数为例,其他聚合函数使用类似。


聚合结果:

案例2 · 订单分析


表数据如下:

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


若 over() 中包含 ORDER BY,不写 BETWEEN...AND... 则默认值为 RANGE BETWEEN unbounded preceding AND current row,与题意不符。

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


(3)统计每个用户每次下单距离上次下单相隔的天数(首次下单按 0 天算)



(4)查询所有下单记录以及每个用户的每个下单记录所在月份的首/末次下单日期


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



当数据量过大时,我们可能需要查找数据子集以加快数据处理速度分析。 这就是抽样、采样,一种用于识别和分析数据中的子集的技术,以发现整个数据集中的模式和趋势。

在 HQL 中,可以通过三种方式采样数据:随机采样、分桶表采样、块采样。

Random 随机抽样使用 rand() 函数和 LIMIT 关键字来获取数据。 使用了 DISTRIBUTE 和 SORT 关键字,可以确保数据也随机分布在 Mapper 和 Reducer 之间,使得底层执行有效率。

ORDER BY 和 rand() 语句也可以达到相同的目的,但是表现不好。因为 ORDER BY 是全局排序,只会启动运行一个 Reducer。



Block 块采样允许 select 随机获取 n 行数据,即数据大小或 n 个字节的数据。

采样粒度是 HDFS 块大小。优点是速度快,缺点是不随机。



Bucket Table 是一种特殊的采样方法,针对分桶表进行了优化。优点是既随机速度也很快。


特别提示:本信息由相关用户自行提供,真实性未证实,仅供参考。请谨慎采用,风险自负。


举报收藏 0评论 0
0相关评论
相关最新动态
推荐最新动态
点击排行
{
网站首页  |  关于我们  |  联系方式  |  使用协议  |  隐私政策  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  鄂ICP备2020018471号