关于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 ...
随机推荐
- How to Read a Paper丨如何阅读一篇论文
这是我在看论文时无意刷到的博客推荐的一篇文章"How to Read a Paper",教你怎么样看论文.对于研究生来说,看论文基本是日常,一篇论文十多二十页,如何高效地读论文确实 ...
- 性能测试-实例讲解VU、RPS、RT公式换算
概述 今天看到一篇文章讲解VU.RPS.RT,中间有一个公式如下图 并发数 = RPS * 响应时间 于是我在本地做了几次实验,试图验证一下公式的准确性 实验网站 www.baidu.com 第一次 ...
- C#3.0新增功能06 对象和集合初始值设定项
连载目录 [已更新最新开发文章,点击查看详细] 使用 C# 可以在单条语句中实例化对象或集合并执行成员分配. 对象初始值设定项 使用对象初始值设定项,你可以在创建对象时向对象的任何可访问字段或属 ...
- 利用DOMNodeInserted监听标签内容变化
var exeFlag = 0;//控制执行业务次数标记$('#list1').bind('DOMNodeInserted', function () { if(!/img/.test($(" ...
- burpsuit用法
1. 学习Proxy首先看标红,intercept is on 为拦截状态 其对应的intercept is off 为非拦截状态,设置完代理后打开拦截状态 ,浏览器发起的请求会被burpsuite ...
- css常用语法续集
1 设置字体 body{font-familly:“宋体”} 2 可以使用下面代码设置网页中文字的字号为12像素,并把字体颜色设置为#666(灰色): body{font-size:12px;c ...
- 勘误:EOS资源抵押退还
关键字:勘误,delegatebw,undelegatebw,listbw,资源管理,抵押,解抵押,返还资源 EOS中,资源抵押与解抵押是通过一对命令完成的:delegatebw,undelegate ...
- thinkphp 插件
1.切换到项目根目录,使用composer require 5ini99/think-addons:dev-master命令安装thinkphp插件 如果是root用户或是管理员执行的话会有提示 等一 ...
- CDN绕过姿势小结
公司的各业务主站都挂了CDN,总结一波CDN绕过技巧. 什么是CDN CDN的全称是Content Delivery Network,即内容分发网络. 其基本思路是尽可能避开互联网上有可能影响数据传输 ...
- C# 委托(delegate)、泛型委托和Lambda表达式
目录 # 什么是委托 # 委托声明.实例化和调用 1.声明 2.委托的实例化 3.委托实例的调用 4.委托完整的简单示例 #泛型委托 1.Func委托 2.Action委托 3.Predicate委托 ...