与RDD类似,DataFrame也是一个分布式数据容器。然而DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。同时,与Hive类似,DataFrame也支持嵌套数据类型(struct、array和map)。从API易用性的角度上 看,DataFrame API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。由于与R和Pandas的DataFrame类似,Spark DataFrame很好地继承了传统单机数据分析的开发体验。
SparkSql 1.x和2.x版本最大的区别在于:
1.x版本通过对SparkContext进行包装增强,创建SQLContext对象,将普通RDD转换成DataFrame;
2.x版本有独立的编程api—SparkSession 作为程序的入口。
编写SparkSql1.x方法有两种:sql和DataFrame的api(DSL)。
方法一:
1.创建sparkContext,然后再创建SQLContext
2.先创建RDD,对数据进行整理,然后关联case class,将非结构化数据转换成结构化数据
3.显示的调用toDF方法将RDD转换成DataFrame
4.注册临时表
5.执行SQL(Transformation,lazy)
6.执行Action
方法二:
1.创建sparkContext,然后再创建SQLContext
2.先创建RDD,对数据进行整理,然后关联Row,将非结构化数据转换成结构化数据
3.定义schema
4.调用sqlContext的createDataFrame方法
5.注册临时表
6.执行SQL(Transformation,lazy)
7.执行Action
/**
*方法一
*/
object SparkSql1 {
def main(args: Array[String]): Unit = {
//提交的这个程序可以连接到Spark集群中
val conf = new SparkConf().setAppName("SparkSql1").setMaster("local[4]")
//创建连接
val sc = new SparkContext(conf)
//sparkContext不能创建特殊的RDD(DataFrame)
//将SparkContext包装进而增强
val sqlContext = new SQLContext(sc)
//创建特殊的RDD(DataFrame),就是有schema信息的RDD
//先有一个普通的RDD,然后再关联上schema,进而转成DataFrame
val lines = sc.textFile("hdfs://node01:8020/person")
//将数据进行整理
val boyRDD: RDD[Boy] = lines.map(line => {
val fields = line.split(",")
val id = fields(0).toLong
val name = fields(1)
val age = fields(2).toInt
var fv = fields(3).toDouble
Boy(id, name, age, fv)
})
//还是一个RDD,有了shcma信息
//将RDD转成DataFrame
//导入隐式转换
import sqlContext.implicits._
val bdf: DataFrame = boyRDD.toDF
//变成df后就可以用两种API进行编程
//把df先注册临时表
bdf.registerTempTable("t_boy")
//书写sql(sql方法其实是Transformation)
val result: DataFrame = sqlContext.sql("SELECT * FROM t_boy ORDER BY fv desc,age asc")
//查看结果 触发action
result.show()
sc.stop()
}
}
case class Boy(id:Long,name: String,age:Int,fv:Double)
/**
*方法一
*/
object SparkSql2 {
def main(args: Array[String]): Unit = {
...
...
// 数据进行整理
val boyRDD: RDD[Row] = lines.map(line => {
val fields = line.split(",")
val id = fields(0).toLong
val name = fields(1)
val age = fields(2).toInt
var fv = fields(3).toDouble
Row(id, name, age, fv)
})
//结果类型,其实就是表头,用于描述DataFrame
val schema= StructType(List(
StructField("id",LongType,true),
StructField("name",StringType,true),
StructField("age",IntegerType,true),
StructField("fv",DoubleType,true)
))
//将ROWRDD关联schema
val bdf: DataFrame = sqlContext.createDataFrame(boyRDD,schema)
//变成df后就可以用两种API进行编程
//把df先注册临时表
bdf.registerTempTable("t_boy")
//书写sql(sql方法其实是Transformation)
val result: DataFrame = sqlContext.sql("SELECT * FROM t_boy ORDER BY fv desc,age asc")
//查看结果 触发action
result.show()
sc.stop()
}
}
//将ROWRDD关联schema
val bdf: DataFrame = sqlContext.createDataFrame(boyRDD,schema)
//不使用sql方式,不需要注册临时表
import sqlContext.implicits._
val df1: DataFrame = bdf.select("name","age")
.orderBy($"fv" desc,$"age" asc)
df1.show()
sc.stop()
注意:SparkSQL 2.x 的编程API和入口都是SparkSession;不需要创建SparkConf和SparkContext
//spark2.x sql 的编程API(SparkSession)
//是spark2.x sql执行入口
val session = SparkSession.builder()
.appName("SparkSQL2Test")
.master("local[*]")
.getOrCreate()
//常见RDD
val lines: RDD[String] = session
.sparkContext
.textFile("hdfs://node01:8020/person")
//将数据进行整理
val boyRDD: RDD[Row] = lines.map(line => {
val fields = line.split(",")
val id = fields(0).toLong
val name = fields(1)
val age = fields(2).toInt
var fv = fields(3).toDouble
Row(id, name, age, fv)
})
//结果类型,其实就是表头,用于描述DataFrame
val schema= StructType(List(
StructField("id",LongType,true),
StructField("name",StringType,true),
StructField("age",IntegerType,true),
StructField("fv",DoubleType,true)
))
//创建DataFrame
val dataFrame: DataFrame = session.createDataFrame(boyRDD,schema)
import session.implicits._
dataFrame.where($"fv">98)
.orderBy($"fv" desc,$"age" asc)
dataFrame.show()
session.stop()