关于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 ...
随机推荐
- 【动态规划例题-数塔问题】-C++
描述 观察下面的数字金字塔.写一个程序查找从最高点到底部任意处结束的路径,使路径经过数字的和最大.每一步可以 从当前点走到左下方的点也可以到达右下方的点. 在上面的样例中,从13到8到26到15到24 ...
- python3.5学习笔记(第六章)
本章内容: 正则表达式详解(re模块) 1.不使用正则表达式来查找文本的内容 要求从一个字符串中查找电话号码,并判断是否匹配制定的模式,如:555-555-5555.传统的查找方法如下: def is ...
- xss magic_quotes_gpc
---恢复内容开始--- magic_quotes_gpc函数,在php5.4以上移除了, 但是很奇怪的是 我的5.6版本这边 是可以找到这个选项的. 在php.ini文件里面,默认关闭,如果将此 ...
- 机器学习-利用pickle加载cifar文件
首先这里有百度云的数据集供大家下载:(官网太慢了) 链接:https://pan.baidu.com/s/1G0MxZIGSK_DyZTcuNbxraQ 提取码:ui51 复制这段内容后打开百度网盘手 ...
- sql nvarchar类型和varchar类型存储中文字符长度
今天遇到了,随手记录一下. sql server 存储数据里面 NVARCHAR 记录中文的时候是 一个中文对应一个字符串长度,记录英文也是一个字母一个长度 标点符号也是一样. ...
- vue教程(四)--其他实用用法补充
一.vue生命周期简单介绍 var App={ template:'', data(){ }, beforeCreated:function(){ //不能操作数据,只是初始化了事件等.. }, cr ...
- 0728 history
-- :: cd /etc/yum.repos.d/ -- :: wget http://download.virtualbox.org/virtualbox/rpm/rhel/virtualbox. ...
- Linu基础之权限管理
二十二.权限管理 22.1)什么是权限 针对某些文件或者进程,对用户进行限制,权限可以理解为用于约束用户能对系统所做的操作. 22.2)权限和用户的关系 [root@centos7 ~]# ll ...
- Servlet的介绍
Servlet由来 做过BS项目的人都知道,浏览器能够根据HTML静态标记语言来显示各式各样的网页.但是如果我们需要在网页上完成一些业务逻辑:比如登陆验证.或者说网页显示的内容在服务器的数据库中.如果 ...
- jboss反序列化漏洞复现(CVE-2017-7504)
jboss反序列化漏洞复现(CVE-2017-7504) 一.漏洞描述 Jboss AS 4.x及之前版本中,JbossMQ实现过程的JMS over HTTP Invocation Layer的HT ...