在Spark中,Dataframe简直可以称为内存中的文本文件。

就像在电脑上直接操作txt、 csv、 json文件一样简单。

val sparkConf = new SparkConf().setAppName("df2db").setMaster("local[1]")

val sc = new SparkContext(sparkConf)

val sqlContext : SQLContext = new SQLContext(sc)

val df = sqlContext.read.format("csv").option("header","true").load("D:\\spark test\\123")

val snapTable = "env0y"

df.registerTempTable(snapTable)

以上寥寥数语就把一个csv文件转为DataFrame并注册为一张临时表了,这时候就可以像操作数据库表一样操作这个snapTable了:

val sql = "SELECT * FROM " + snapTable

val dfTmp = this.sqlContext.sql(sql)

这样写代码方便简单,但可惜的是DataFrame毕竟仅仅存在于内存中,我们业务代码只会输出算法里规定的结果

也就是说,假如结果出错,不好定位到底是DataFrame本身数据有误,还是代码中的SQL写错了。。。

假如能随时随地操作DataFrame就好了,怎么办呢?

把DataFrame保存到真实的数据库去:

import java.util.Properties

val connectionUrl = "jdbc:sqlserver://10.119.46.153:1433"

val table = "Nettransmit.dbo.df2mssql"

val prop = new Properties()

prop.put("JDBC.Driver","com.microsoft.sqlserver.jdbc.SQLServerDriver")

prop.put("user", "sa")

prop.put("password", "ibas.1597")

val dfWriter = df.write.mode(SaveMode.Overwrite).jdbc(connectionUrl, table, prop)

这下好了,如果计算出错了,我们直接连上数据库几条sql就能debug个八九不离十。

唯一要注意的是,DataFrame to Database不是业务要求,所以上面的代码只能在开发模式或者测试模式的时候存在,正式发布版不应该出现 

既然可以写进去,自然也可以读出来:

//SqlServer 2 Dataframe

val dfviatable = sqlContext.read.jdbc(connectionUrl,table,prop)

dfviatable.show(10)

以上,DataFrame和数据库之间的极简交互就完成了,但如果业务中真的有读写数据库的需求了,性能问题可能会成为瓶颈,要注意的。

接下来是那么一点点优化。

从csv到DataFrame,我们使用df.printSchema()语句可以在控制台看到类似下面的输出:

root

|-- IMSI: string (nullable = true)

|-- UserType: string (nullable = true)

|-- Total PS Traffic(KB): string (nullable = true)

|-- Total Online Time(s): string (nullable = true)

|-- Total CS Traffic (ERL): string (nullable = true)

|-- Brand: string (nullable = true)

|-- Series: string (nullable = true)

|-- OS: string (nullable = true)

|-- Type: string (nullable = true)

|-- FDD LTE: string (nullable = true)

|-- TDD LTE: string (nullable = true)

|-- Only Report 3G Capability: string (nullable = true)

也就是说,写入到数据库之后每个字段的类型都是string,这显然是一种浪费。

而且很多值完全可以使用int或者double或者bool类型。

怎么办呢?得修改数据库的“方言”,就像在c++中std::locale 建立本地规则一样。

为了方便起见,封装一下:

import java.io.{File, FileInputStream}

import java.util.Properties

import org.apache.spark.sql.jdbc.{JdbcDialect, JdbcDialects, JdbcType}

import org.apache.spark.sql.types._

import org.apache.spark.sql.{DataFrame, SaveMode}

/**

* Created by env0y on 2017/11/24.

*/

object dataframe2db {

def df2db(df: DataFrame,table: String,properties: String) = {

try{

val is = new FileInputStream(new File(properties))

val prop = new Properties()

prop.load(is)

val url = String.valueOf(prop.get("url"))//

JdbcDialects.registerDialect(SQLServerDialect)

df.write.mode(SaveMode.Overwrite).jdbc(url,table,prop)

is.close()

}

}

val SQLServerDialect = new JdbcDialect {

override def canHandle(url: String): Boolean = url.startsWith("jdbc:jtds:sqlserver") || url.contains("sqlserver")

override def getJDBCType(dt: DataType): Option[JdbcType] = dt match {

case StringType => Some(JdbcType("NVARCHAR(128)", java.sql.Types.VARCHAR))

case BooleanType => Some(JdbcType("BIT(1)", java.sql.Types.BIT))

case IntegerType => Some(JdbcType("INTEGER", java.sql.Types.INTEGER))

case LongType => Some(JdbcType("BIGINT", java.sql.Types.BIGINT))

case DoubleType => Some(JdbcType("DOUBLE PRECISION", java.sql.Types.DOUBLE))

case FloatType => Some(JdbcType("REAL", java.sql.Types.REAL))

case ShortType => Some(JdbcType("INTEGER", java.sql.Types.INTEGER))

case ByteType => Some(JdbcType("INTEGER", java.sql.Types.INTEGER))

case BinaryType => Some(JdbcType("BINARY", java.sql.Types.BINARY))

case DateType => Some(JdbcType("DATE", java.sql.Types.DATE))

case TimestampType => Some(JdbcType("DATE", java.sql.Types.DATE))

// case DecimalType.Fixed(precision, scale) => Some(JdbcType("NUMBER(" + precision + "," + scale + ")", java.sql.Types.NUMERIC))

case t: DecimalType => Some(JdbcType(s"DECIMAL(${t.precision},${t.scale})", java.sql.Types.DECIMAL))

case _ => throw new IllegalArgumentException(s"Don't know how to save ${dt.json} to JDBC")

}

}

}

然后像这样调用:

dataframe2db.df2db(df,"Nettransmit.dbo.df2dbff","D:\\ database.properties")

第三个参数是数据库的属性配置文件,内容类似以下:

#\u5F00\u53D1\u6570\u636E\u5E93

driver=com.microsoft.sqlserver.jdbc.SQLServerDriver

url=jdbc:sqlserver://10.119.46.153:1433;databaseName=TspManagement

username=sa

password=ibas.1597

这时候再去观察从DataFrame写入到数据库中表会发现,字段属性都变成NVARCHAR(128)了~~

另外,直接修改DataFrame里面的Schema类型也很简单:

val df1 = df.withColumn("Only Report 3G Capability",col("Only Report 3G Capability").cast(DataTypes.FloatType))

df1.printSchema()

就这些,以上Spark的版本是1.6. 涉及的数据库是sqlServer.

DataFrame与数据库的相互转化的更多相关文章

  1. spark RDD,DataFrame,DataSet 介绍

    弹性分布式数据集(Resilient Distributed Dataset,RDD) RDD是Spark一开始就提供的主要API,从根本上来说,一个RDD就是你的数据的一个不可变的分布式元素集合,在 ...

  2. r语言与dataframe

    什么是DataFrame 引用 r-tutor上的定义: DataFrame 是一个表格或者类似二维数组的结构,它的各行表示一个实例,各列表示一个变量. 没错,DataFrame就是类似于Excel表 ...

  3. RDD、DataFrame和DataSet的区别

    原文链接:http://www.jianshu.com/p/c0181667daa0 RDD.DataFrame和DataSet是容易产生混淆的概念,必须对其相互之间对比,才可以知道其中异同. RDD ...

  4. RDD、DataFrame和DataSet

    简述 RDD.DataFrame和DataSet是容易产生混淆的概念,必须对其相互之间对比,才可以知道其中异同:DataFrame多了数据的结构信息,即schema.RDD是分布式的 Java对象的集 ...

  5. HibernateTools实现pojo类 数据库schma mapping映射的相互转换 二

    接着上一篇博客:HibernateTools实现pojo类 数据库schma mapping映射的相互转换 思路二:由数据库表,生成Mapping映射文件和POJO类. 尽管能够实现,但个人觉着先设计 ...

  6. SparkSQL 中 RDD 、DataFrame 、DataSet 三者的区别与联系

    一.SparkSQL发展: Shark是一个为spark设计的大规模数据仓库系统,它与Hive兼容      Shark建立在Hive的代码基础上,并通过将Hive的部分物理执行计划交换出来(by s ...

  7. Sprk SQL

    一.Spark SQL概述  1.Spark SQL的前生今世 Shark是一个为Spark设计的大规模数据仓库系统,它与Hive兼容.Shark建立在Hive的代码基础上,并通过将Hive的部分物理 ...

  8. spark 三种数据集的关系(二)

    一个Dataset是一个分布式的数据集,而且它是一个新的接口,这个新的接口是在Spark1.6版本里面才被添加进来的,所以要注意DataFrame是先出来的,然后在1.6版本才出现的Dataset,提 ...

  9. python数据分析入门学习笔记

    学习利用python进行数据分析的笔记&下星期二内部交流会要讲的内容,一并分享给大家.博主粗心大意,有什么不对的地方欢迎指正~还有许多尚待完善的地方,待我一边学习一边完善~ 前言:各种和数据分 ...

随机推荐

  1. 【剑指Offer】6、旋转数组的最小数字

      题目描述:   把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5 ...

  2. redis安装配置-linux

    wget http://download.redis.io/releases/redis-3.2.9.tar.gz .tar.gz cd redis-/ make --启动 ./redis-serve ...

  3. 《编程导论(Java)·2.1.2 啊,我看到了多态》-什么是多态(polymorphism)

    1.不明觉厉 很多人学习多态时,会认为. 之所以不明觉厉,由于多态的定义:事物存在的多种表现形态:而后,有人将重载(overload).改写(override).多态变量和泛型归结于同一个术语&quo ...

  4. 【Java】基本类型和引用类型(值传递)

    [关键词] [问题] · 加深对基本类型和引用类型的理解: [效果图] [分析] 參见最后的[參考资料] [解决方式] [代码] public void test() throws Exception ...

  5. solr实战-(一)

    实现用户数据索引及查询 1. 启动solr       solr start 2. 创建collection       solr create -c user 3. schema中加入field   ...

  6. Android SQLite服务--创建、增删改查

    <pre name="code" class="java">import android.content.Context; import andro ...

  7. oralce sql 创建指定时间段内的日历信息

    -- Create table create table TEMP_CALENDAR (   MONTH VARCHAR2(6),   W_7   VARCHAR2(2),   W_1   VARCH ...

  8. Running the app on your device

    So far, you've run the app on the Simulator. That's nice and all but probably notwhy you're learning ...

  9. 好吧,我承认我喜欢这种多个 StoryBoard 组织的方式,学习了!

    下面转载内容非常不错.兴许补充从官方文档疏理出来的脉络,确实非常好的使用方法. tid-270505.html"> tid-270505.html">Storyboar ...

  10. C++数组类模板

    * 作为数组类模板,肯定没有vector做得好,可是普通的数组有1个优点就是能直接操作内存.vector在这方面就不是非常方便了. 网上尽管也有数组类模板.多维的设计基本上都不是非常好.我这个类模板多 ...