关于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 ...
随机推荐
- Tomcat CGIServlet enableCmdLineArguments远程代码执行_CVE-2019-0232漏洞复现
Tomcat CGIServlet enableCmdLineArguments远程代码执行_CVE-2019-0232漏洞复现 一.漏洞描述 该漏洞是由于tomcat CGI将命令行参数传递给Win ...
- DataNode的工作机制
DataNode的工作机制 一个数据块在DataNode以文件的形式在磁盘上保存,分为两个文件,一个是数据本身, 一个是元数据信息(包括数据的长度,校验和,时间戳) 1.DataNode启动后,向Na ...
- win10 安装mysql(图文详情)避免卡在最后一步
win10 安装mysql 本人安装mysql一般都是在镜像文件夹中下载(http://mirrors.sohu.com/),最近系统重做了之后安装mysql5.5.msi,可以安装成功.但是在最后的 ...
- HashSet源码解读
一:先看其实现了哪些接口和继承了哪些类 1.实现了Serializable接口,表明它支持序列化. 2.实现了Cloneable接口,表明它支持克隆,可以调用超类的clone()方法进行浅拷贝. 3. ...
- oracle查询截至到当前日期月份所在年份的所有月份
SELECT to_number(TO_CHAR(add_months(trunc(sysdate, 'yy'), ROWNUM - 1), 'MM')) as month FROM DUALCONN ...
- Ubuntu18.04服务器使用netplan网络构建桥接kvm虚拟机
参考链接 Ubuntu 18.04 LTS安装KVM虚拟机 如何在 Ubuntu 18.04 服务器上安装和配置 KVM KVM日常管理和克隆 KVM详解 1.准备工作 首先需要检查一下CPU是否支持 ...
- 【iOS】UIAlertController 弹出框
UIAlertView 虽然还能用,但已经废弃了.因此以后尽量用 UIAlertController.示例代码如下: UIAlertController *alert = [UIAlertContro ...
- 非UI线程更新界面
package com.caterSys.Thread; import java.text.SimpleDateFormat; import java.util.Date; import org.ec ...
- mysql是如何实现事务隔离以及MVCC详解
提到事务,你肯定会想到ACID(Atomicity.Consistency.Isolation.Durability,即原子性.一致性.隔离性.持久性),我们就来说说其中I,也就是"隔离性& ...
- Java NIO学习系列七:Path、Files、AsynchronousFileChannel
相对于标准Java IO中通过File来指向文件和目录,Java NIO中提供了更丰富的类来支持对文件和目录的操作,不仅仅支持更多操作,还支持诸如异步读写等特性,本文我们就来学习一些Java NIO提 ...