详解Spark sql用户自定义函数:UDF与UDAF
UDAF = USER DEFINED AGGREGATION FUNCTION
Spark sql提供了丰富的内置函数供猿友们使用,辣为何还要用户自定义函数呢?实际的业务场景可能很复杂,内置函数hold不住,所以Spark sql提供了可扩展的内置函数接口:哥们,你的业务太变态了,我满足不了你,自己按照我的规范去定义一个sql函数,该怎么折腾就怎么折腾!
例如,MySQL数据库中有一张task表,共两个字段taskid (任务ID)与taskParam(JSON格式的任务请求参数)。简单起见,这里只列出一条记录:
taskid taskParam {"endAge":[""],"endDate":["2016-06-21"],"startAge":[""],"startDate":["2016-06-21"]}
假设应用程序已经读取了MySQL中这张表的记录,并通过 DateFrame注册成了一张临时表 task。问题来了:怎么获取taskParam中startAge的第一个值呢?
sqlContext.sql("select taskid,getJsonFieldUDF(taskParm,'startAge')")
这个时候,我们就需要自定义一个UDF函数了,取名getJsonFieldUDF。Java版本的代码大致如下:
package cool.pengych.sparker.product;
import org.apache.spark.sql.api.java.UDF2;
import com.alibaba.fastjson.JSONObject;
/**
* 用户自定义函数
* @author pengyucheng
*/
public class GetJsonObjectUDF implements UDF2<String,String,String>
{
/**
* 获取数组类型json字符串中某一字段的值
*/
@Override
public String call(String json, String field) throws Exception
{
try
{
JSONObject jsonObject = JSONObject.parseObject(json);
return jsonObject.getJSONArray(field).getString();
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
}
这样的需求在实际项目中是很普遍的:请求参数经常以json格式存储在数据库中。这里还是先以Scala实现一个简单的hello world级别的小样为例,来体验udf与udaf的使用好了。
问题
将如下数组:
val bigData = Array("Spark","Hadoop","Flink","Spark","Hadoop","Flink",
"Spark","Hadoop","Flink","Spark","Hadoop","Flink")
中的字符分组聚合并计算出每个字符的长度及字符出现的个数。正常结果
如下:
+------+-----+------+
| name|count|length|
+------+-----+------+
| Spark| | |
| Flink| | |
|Hadoop| | |
+------+-----+------+
注:‘spark’ 这个字符的长度为5 ,共出现了4次。
分析
- 自定义个一个求字符串长度的函数
自定义的sql函数,与scala中的普通函数一样,只不过在使用上前者需要先在sqlContext中进行注册。 - 自定义一个聚合函数
按照字符串名称分组后,调用自定义的聚合函数实现累加。
啊,好抽象,直接看代码吧!
代码
package main.scala import org.apache.spark.SparkContext
import org.apache.spark.SparkConf
import org.apache.spark.sql.hive.HiveContext
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.StringType
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.expressions.UserDefinedAggregateFunction
import org.apache.spark.sql.types.IntegerType
import org.apache.spark.sql.types.DataType
import org.apache.spark.sql.expressions.MutableAggregationBuffer /**
* Spark SQL UDAS:user defined aggregation function
* UDF: 函数的输入是一条具体的数据记录,实现上讲就是普通的scala函数-只不过需要注册
* UDAF:用户自定义的聚合函数,函数本身作用于数据集合,能够在具体操作的基础上进行自定义操作
*/
object SparkSQLUDF { def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local[*]").setAppName("SparkSQLWindowFunctionOps")
val sc = new SparkContext(conf) val hiveContext = new SQLContext(sc) val bigData = Array("Spark","Hadoop","Flink","Spark","Hadoop","Flink","Spark","Hadoop","Flink","Spark","Hadoop","Flink")
val bigDataRDD = sc.parallelize(bigData) val bigDataRowRDD = bigDataRDD.map(line => Row(line))
val structType = StructType(Array(StructField("name",StringType,true)))
val bigDataDF = hiveContext.createDataFrame(bigDataRowRDD, structType) bigDataDF.registerTempTable("bigDataTable") /*
* 通过HiveContext注册UDF,在scala2.10.x版本UDF函数最多可以接受22个输入参数
*/
hiveContext.udf.register("computeLength",(input:String) => input.length)
hiveContext.sql("select name,computeLength(name) as length from bigDataTable").show //while(true){} hiveContext.udf.register("wordCount",new MyUDAF)
hiveContext.sql("select name,wordCount(name) as count,computeLength(name) as length from bigDataTable group by name ").show
}
} /**
* 用户自定义函数
*/
class MyUDAF extends UserDefinedAggregateFunction
{
/**
* 指定具体的输入数据的类型
* 自段名称随意:Users can choose names to identify the input arguments - 这里可以是“name”,或者其他任意串
*/
override def inputSchema:StructType = StructType(Array(StructField("name",StringType,true))) /**
* 在进行聚合操作的时候所要处理的数据的中间结果类型
*/
override def bufferSchema:StructType = StructType(Array(StructField("count",IntegerType,true))) /**
* 返回类型
*/
override def dataType:DataType = IntegerType /**
* whether given the same input,
* always return the same output
* true: yes
*/
override def deterministic:Boolean = true /**
* Initializes the given aggregation buffer
*/
override def initialize(buffer:MutableAggregationBuffer):Unit = {buffer()=} /**
* 在进行聚合的时候,每当有新的值进来,对分组后的聚合如何进行计算
* 本地的聚合操作,相当于Hadoop MapReduce模型中的Combiner
*/
override def update(buffer:MutableAggregationBuffer,input:Row):Unit={
buffer() = buffer.getInt()+
} /**
* 最后在分布式节点进行local reduce完成后需要进行全局级别的merge操作
*/
override def merge(buffer1:MutableAggregationBuffer,buffer2:Row):Unit={
buffer1() = buffer1.getInt()+buffer2.getInt()
} /**
* 返回UDAF最后的计算结果
*/
override def evaluate(buffer:Row):Any = buffer.getInt()
}
执行结果:
// :: INFO DAGScheduler: ResultStage (show at SparkSQLUDF.scala:) finished in 1.625 s
+------+-----+------+
| name|count|length|
+------+-----+------+
| Spark| | |
| Flink| | |
|Hadoop| | |
+------+-----+------+ // :: INFO DAGScheduler: Job finished: show at SparkSQLUDF.scala:, took 1.717878 s
总结
呼叫spark大神升级udaf实现
为了自己实现一个sql聚合函数,我需要继承UserDefinedAggregateFunction并实现8个抽象方法!8个方法啊!what’s a disaster ! 然而,要想在sql中完成符合特定业务场景的聚合类(a = aggregation)功能,就得udaf。
怎么理解MutableAggregationBuffer呢?就是存储中间结果的,聚合就意味着多条记录的累加等操作。udf与udaf注册语法
hiveContext.udf.register("computeLength",(input:String) => input.length)
hiveContext.udf.register("wordCount",new MyUDAF)
详解Spark sql用户自定义函数:UDF与UDAF的更多相关文章
- Spark SQL 用户自定义函数UDF、用户自定义聚合函数UDAF 教程(Java踩坑教学版)
在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_date等 UDAF( ...
- SQL 中详解round(),floor(),ceiling()函数的用法和区别?
SQL 中详解round(),floor(),ceiling()函数的用法和区别? 原创 2013年06月09日 14:00:21 摘自:http://blog.csdn.net/yueliang ...
- Hive 文件格式 & Hive操作(外部表、内部表、区、桶、视图、索引、join用法、内置操作符与函数、复合类型、用户自定义函数UDF、查询优化和权限控制)
本博文的主要内容如下: Hive文件存储格式 Hive 操作之表操作:创建外.内部表 Hive操作之表操作:表查询 Hive操作之表操作:数据加载 Hive操作之表操作:插入单表.插入多表 Hive语 ...
- Spark SQL 自定义函数类型
Spark SQL 自定义函数类型 一.spark读取数据 二.自定义函数结构 三.附上长长的各种pom 一.spark读取数据 前段时间一直在研究GeoMesa下的Spark JTS,Spark J ...
- Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕
Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕 今天会逐行解析一下SparkStreaming运行的日志,运行的是WordCountO ...
- mybatis 详解------动态SQL
mybatis 详解------动态SQL 目录 1.动态SQL:if 语句 2.动态SQL:if+where 语句 3.动态SQL:if+set 语句 4.动态SQL:choose(when,o ...
- 第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样?
第7.25节 Python案例详解:使用property函数定义与实例变量同名的属性会怎样? 一. 案例说明 我们上节提到了,使用property函数定义的属性不要与类内已经定义的普通实例变量重 ...
- 第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现
第7.24节 Python案例详解:使用property函数定义属性简化属性访问代码实现 一. 案例说明 本节将通过一个案例介绍怎么使用property定义快捷的属性访问.案例中使用Rectan ...
- hive自定义函数UDF UDTF UDAF
Hive 自定义函数 UDF UDTF UDAF 1.UDF:用户定义(普通)函数,只对单行数值产生作用: UDF只能实现一进一出的操作. 定义udf 计算两个数最小值 public class Mi ...
随机推荐
- NodeJS与Javascript时代
如果你一直在关注互联网的相关技术,你应该会有这样一种感觉,web技术正在发生着变革,虽然我们不愿相信,但一个事实已经越来越清晰的摆在了眼前:LAMP组合的时代将要成为历史,在web诞生的二十年间,它影 ...
- 硬件设计之串口收发器---ISO1050 (现行) 隔离式 5V CAN 收发器
http://www.ti.com.cn/product/cn/iso1050 http://www.deyisupport.com/question_answer/dsp_arm/sitara_ar ...
- ACM计算几何模板——二维几何基础(基本运算,点和线,多边形)
/*==========================*\ | 计算几何基础函数 | | 1.点和向量的定义 | | 2.向量的基本运算 | | 3.点积 | | 4.向量长度 | | 5.两向量角 ...
- 用公式编辑器编辑n元乘积的方法
在数学中经常会出现很多个元素进行求和或者是乘积的情况,但是在整个数学过程中,不可能将所有的元素都写出来,这样很费时费力同时过程也很赘余,不能很好地理解其中的过程,因此数学中对于这一类的多元相加或者相乘 ...
- Python+PyQt5:停靠组件QDockWidget的实现
QMainWindow主窗体中放置停靠组件QDockWidget的实现流程: 第一步:创建QMainWindow窗体,这是承载QDockWidget的主体 第二步:创建QDockWidget组件实例, ...
- WebApi 异常处理解决方案
1.继承ExceptionFilterAttribute类,重写OnException方法 public class WebApiExceptionFilterAttribute : Exceptio ...
- Android设计模式之单例模式的七种写法
一 单例模式介绍及它的使用场景 单例模式是应用最广的模式,也是我最先知道的一种设计模式.在深入了解单例模式之前.每当遇到如:getInstance()这样的创建实例的代码时,我都会把它当做一种单例模式 ...
- iOS开发之--png图片编译时报错 (Command /Applications/Xcode.app/Contents/Developer/usr/bin/copypng failed with exit code 1 )
编译或者运行APP的时候,老是报这个错误:Command /Applications/Xcode.app/Contents/Developer/usr/bin/copypng failed with ...
- Hadoop1.2.1 日志格式说明及启停方式
日志格式: 日志名称解析: Hadoop启停的三种方式: . 停止后面的以此类推...... 另外 hadoop-daemons.sh 表示启动多个,比如datanode跟tasktracker在真实 ...
- soundpool播放声音
一般大家使用的是MediaPlayer来播放音频,它的创建和销毁都是非常消耗资源的,如果我们的需求是播放一些短促而且频繁播放的音频的话MediaPlayer就有些不合适了,我们来讲讲SoundPool ...