关于sparksql中设置自定义自增列的相关要点(工作共踩过的坑-1)
小白终于进入了职场,从事大数据方面的工作!
分到项目组了,搬砖的时候遇到了一个这样的问题。
要求:用spark实现oracle的存储过程中计算部分。
坑:由于报表中包含了一个ID字段,其要求是不同的区域拥有不同的区域ID,且ID在数据库表中的属性为主键。Oracle的存储过程中采用的是自定义序列,采用发号的形式实现ID唯一且符合区域特性。
填坑过程:
方法一:sql.functions 中monotonically_increasing_id
。
采用import org.apache.spark.sql.functions.中的
monotonically_increasing_id函数。
使用demo如下:
//从数据库中加载表TEST_EMP进入内存,并且取ENAME和EMPNO两列
val dfEmp=sqlContext.read.options(conUtil.con("TEST_EMP"))
.format("jdbc").load()
.select("ENAME","EMPNO")
val test =dfEmp
.withColumn("TEST_NO",monotonically_increasing_id)
//向oracle中写数据,这个函数的使用前提是需要确定表"EMP_TMP"存在。且向这张表写入数据的时候最好字段进行对应,如果列多余数据库中的列数则会出现参数过多的错误。
JdbcUtils.saveTable(test, url, "EMP_TMP", properties) //代码结果如下所示,在数据库中生成了一个从0开始自增的列
ENAME | EMPNO | TEST_NO |
SMITH | 7369 | 0 |
ALLEN | 7499 | 1 |
WARD | 7521 | 2 |
JONES | 7566 | 3 |
这个方法有一个缺点:序列是从0开始的,monotonically_increasing_id函数无法接受参数,所以我们无法用其根据我们的业务进行指定序列。
所以,有一个想法于是去看了一下该方法的源码,发下如下特点:
首先看到函数的定义def monotonically_increasing_id(): Column = withExpr { MonotonicallyIncreasingID() }
深入查看MonotonicallyIncreasingID() ,具体源码如下:
private[sql] case class MonotonicallyIncreasingID() extends LeafExpression with Nondeterministic { /**
* Record ID within each partition. By being transient, count's value is reset to 0 every time
* we serialize and deserialize and initialize it.
*/
@transient private[this] var count: Long = _ @transient private[this] var partitionMask: Long = _ override protected def initInternal(): Unit = {
count = 0L
partitionMask = TaskContext.getPartitionId().toLong << 33
} override def nullable: Boolean = false override def dataType: DataType = LongType override protected def evalInternal(input: InternalRow): Long = {
val currentCount = count
count += 1
partitionMask + currentCount
} override def genCode(ctx: CodeGenContext, ev: GeneratedExpressionCode): String = {
val countTerm = ctx.freshName("count")
val partitionMaskTerm = ctx.freshName("partitionMask")
ctx.addMutableState(ctx.JAVA_LONG, countTerm, s"$countTerm = 0L;")
ctx.addMutableState(ctx.JAVA_LONG, partitionMaskTerm,
s"$partitionMaskTerm = ((long) org.apache.spark.TaskContext.getPartitionId()) << 33;") ev.isNull = "false"
s"""
final ${ctx.javaType(dataType)} ${ev.value} = $partitionMaskTerm + $countTerm;
$countTerm++;
"""
}
}
我们可以发现这个类中重写了父类的initInternal()方法,指定了初始值count=0L,enmm这样子的话我们可不可以通过复写该类中的初始值来满足我们的业务需求
override protected def initInternal(): Unit = {
count = 0L
partitionMask = TaskContext.getPartitionId().toLong << 33
}
(别想太多,一个业务涉及那么多序列,总不能用一次改一次吧,当然如果技术过硬,自己写一套方法以及类,用来接收参数1:序列起始值,参数2:序列终止值。当前技术不够且加班 导致这个想法凉凉)
方法二:rdd算子中的zipWithIndex()方法
代码demo如下:
val dfEmp=sqlContext.read.options(conUtil.con("TEST_EMP"))
.format("jdbc").load()
.select("ENAME","EMPNO")
//对读取的dfEmp进行schema加列操作,增加一列且指定列数据类型
val schma=dfEmp.schema.add(StructField("TEST_NO",LongType))
val temp=dfEmp.rdd.zipWithIndex()
//可以在row中指定我们自己业务需求的序列初始值
val changed= temp.map(t => Row.merge(t._1, Row(t._2+340000000)))
val in=sqlContext.createDataFrame(changed,schma)
JdbcUtils.saveTable(in, url, "EMP_TMP", properties)
结果如下所示:
ENAME | EMPNO | TEST_NO |
SMITH | 7369 | 300000000 |
ALLEN | 7499 | 300000001 |
WARD | 7521 | 300000002 |
到此,入职的第一个坑填好了!貌似方法二还能够用zipWithUniqueId()方法进行实现,由于时间不够就没有一一的尝试了,如果各位小伙伴们有空可以尝试一下!
同时,如果小伙伴们有更加好的方法,求分享!求指导!感谢!!!!!
欢迎留言!!!!
关于sparksql中设置自定义自增列的相关要点(工作共踩过的坑-1)的更多相关文章
- SpringBoot中设置自定义拦截器
SpringBoot中设置自动以拦截器需要写一个类继承HandlerInterceptorAdapter并重写preHandle方法 例子 public class AuthorityIntercep ...
- SparkSQL中的自定义函数UDF
在Spark中,也支持Hive中的自定义函数.自定义函数大致可以分为三种: UDF(User-Defined-Function),即最基本的自定义函数,类似to_char,to_date等 UDAF( ...
- 在python脚本中设置环境变量,并运行相关应用
1. 问题 在自动化应用的时候 ,有时候环境变量与运行需要不一致.这时候有两种选择: 改变节点环境变量,使得其和运行需求保持一致: 在自动化脚本中设置环境变量,其范围只在脚本运行环境中有效. 显然,当 ...
- 在ListCtrl控件中设置自定义光标
::SetCursor(::LoadCursor (::AfxGetInstanceHandle(), MAKEINTRESOURCE(IDB_BMP_MOUSE))); void CMy ...
- eclipse中设置自定义文档签名(工具)
今天第一次认真学习eclipse的使用,看到自定义文档签名,步骤如下: 1.点击window->preferences->java->Code Style->Code Tem ...
- Eclipse中设置自定义文档签名
今天第一次认真学习eclipse的使用,看到自定义文档签名,步骤如下: 1.点击window->preferences->java->Code Style->Code Temp ...
- Linux系统入门学习:在curl中设置自定义的HTTP头
http://www.linuxidc.com/Linux/2015-02/114220.htm
- (转)Repeater中增加序号自增列
<%# Convert.ToString(Container.ItemIndex+)%> 当Repeater空为时,提示没有数据... <FooterTemplate> < ...
- PowerDesigner中如何生成主键和自增列
1.SQL Server版本: 第一步,首先要建立与数据库的连接,方法较多,这里举个例子: http://www.cnblogs.com/netsql/archive/2010/05/17/17375 ...
随机推荐
- 码云及Git的使用
什么是码云 码云就是相当一个远程仓库,在以后的工作中,你和同事负责工作的不同部分,齐头并进,最后上传到码云,类似于一个汇总的作用. 同一个绳上的不同分支 码云网址链接:https://gitee.co ...
- [leetcode] 650. 2 Keys Keyboard (Medium)
解法一: 暴力DFS搜索,对每一步进行复制还是粘贴的状态进行遍历. 注意剪枝的地方: 1.当前A数量大于目标数量,停止搜索 2.当前剪贴板数字大于等于A数量时,只搜索下一步为粘贴的状态. Runtim ...
- mysql8.0.15创建数据库和是删除数据库及用户删除
1.首先安装mysql8.0.15 2.Mysql8.0.15安装成功后,默认的root用户密码为空,用以下命令来登录root用户: mysql –u root –p 记住密码不用输入 3.进入之后修 ...
- Android Studio电脑不支持HAXM的解决办法
Intel HAXM is required to run this AVD. Your CPU does not support required features (VT-x or SVM). U ...
- 算法与数据结构基础 - 广度优先搜索(BFS)
BFS基础 广度优先搜索(Breadth First Search)用于按离始节点距离.由近到远渐次访问图的节点,可视化BFS 通常使用队列(queue)结构模拟BFS过程,关于queue见:算法与数 ...
- 【JDK】JDK源码分析-ArrayList
概述 ArrayList 是 List 接口的一个实现类,也是 Java 中最常用的容器实现类之一,可以把它理解为「可变数组」. 我们知道,Java 中的数组初始化时需要指定长度,而且指定后不能改变. ...
- 调用ffmpeg视频压缩工具类
package com.example.demo; import com.alibaba.fastjson.JSONObject;import com.aliyun.oss.ClientExcepti ...
- VSTO之PowerPoint(PPT)插件开发常用API汇总
VSTO简介 VSTO(Visual Studio Tools for Office )是VBA的替代,使得开发Office应用程序更加简单,并且用VSTO来开发office应用程序可以使用Visua ...
- String常量池和intern方法
String s1 = "Hello"; String s2 = "Hello"; String s3 = "Hel" + "lo ...
- webpack4核心模块tapable源码解析
_ 阅读目录 一:理解Sync类型的钩子 1. SyncHook.js 2. SyncBailHook.js 3. SyncWaterfallHook.js 4. SyncLoopHook.js 二: ...