一、DataFrame和DataSet
DataFrame是一个分布式数据容器,除了记录数据以外,还记录数据的结构信息。
Dataset是一个由特定领域的对象组成强类型(typedrel)集合,可以使用函数(DSL)或关系运算(SQL)进行并行的转换操作。
Dataset可以认为是DataFrame的一个特例,并且Dataset和DataFrame都是懒加载的,只有触发行动算子才会执行操作。
二、创建sparkSQL的运行环境
首先引入maven项目
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.12</artifactId>
<version>3.1.1</version>
</dependency>
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("sql").setMaster("local")
// 由于sparksession的构造方法是私有的所以不能通过new的方式,sparkSession底层封装了sparkContext
val session: SparkSession = SparkSession.builder().config(conf).getOrCreate()
session.close()
}
三、创建DataFrame,DataSet
创建DataFrame以及两种操作方式。
// 创建dataFrame type DataFrame = Dataset[Row]就是一个特殊类型的dataSet
val df: DataFrame = session.read.json("datas/user.json")
df.show()
// 通过sql的方式操纵df
df.createGlobalTempView("globalUser") //创建全局视图
df.createOrReplaceTempView("user") // 创建临时视图
session.sql("select * from user").show() // 直接写sql语句去查询视图,show是展示结果
// 通过sdl的方式操纵df
import session.implicits._ // 引入转换规则,否则不识别$,也可以用'代替$
// as 是别名,filter 就是类似where操作
df.select($"id" + 1 as "pp").filter($"damage" > 90).show
创建DataSet
import session.implicits._ // 这里是前面的引入规则
val datas = Seq(1, 2, 3, 4)
val dataSet: Dataset[Int] = datas.toDS()
dataSet.show()
四、DataFrame、DataSet、RDD的相互转换
// RDD转换成dataframe
val rdd = session.sparkContext.makeRDD(List((1, "zhangsan", 30),(3, "wangwu", 40)))
val dataFrame = rdd.toDF("id", "name", "age") // 转换成DF,里面传列名
val changeRdd: RDD[Row] = dataFrame.rdd // dataframe转换成rdd
// dataframe转换成dataSet
val ds = dataFrame.as[User] // 根据样例类来进行转换
val changeFrame = ds.toDF() // 转换成changeFrame
// rdd转换dataSet 需要首先给dataSet进行转换
val ds1 = rdd.map(item => {
User(item._1, item._2, item._3)
}).toDS()
val rdd1 = ds1.rdd // 转换成RDD
// 提供数据结构的样例类
case class User(var id:Int, var name:String, var age:Int)
五、用户自定义函数
第一种姿势
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("sql").setMaster("local")
val session: SparkSession = SparkSession.builder().config(conf).getOrCreate()
import session.implicits._
val dataFrame = session.read.json("datas/user.json")
dataFrame.createOrReplaceTempView("user")
// 通过udf注册自定义函数,两个参数:函数名和实现
session.udf.register("prefix", (name: String) => {"zyz"+name})
session.sql("select prefix(damage) from user").show()
session.close()
}
弱类型的姿势:直接修改sql语句,现在已经不推荐使用了
object Sql01 {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("sql").setMaster("local")
val session: SparkSession = SparkSession.builder().config(conf).getOrCreate()
import session.implicits._
val dataFrame = session.read.json("datas/user.json")
dataFrame.createOrReplaceTempView("user")
// 通过udf注册自定义类
session.udf.register("prefix", new MyUAF)
session.sql("select prefix(damage) from user").show()
session.close()
}
// 弱类型方式自定义类 弱类型是指修改sql语句,现在不推荐了
class MyUAF extends UserDefinedAggregateFunction{
// 输入结构
override def inputSchema: StructType = {
StructType(Array(StructField("damage", IntegerType)))
}
// 缓冲区做计算的结构, 放中间变量和最后变量
override def bufferSchema: StructType = {
StructType(Array(StructField("total", IntegerType), StructField("count", IntegerType)))
}
// 计算结果的数据类型
override def dataType: DataType = IntegerType
// 函数的稳定性,传入相同的参数结果是否相同
override def deterministic: Boolean = true
// 缓冲区初始化
override def initialize(buffer: MutableAggregationBuffer): Unit = {
buffer.update(0, 0) // 第一个参数代表索引位置,第二个代表初始值
buffer.update(1, 0)
}
// 每次读入数据缓冲区如何更新
override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
buffer.update(0, buffer.getInt(0) + input.getInt(0))
buffer.update(1, buffer.getInt(1) + 1)
}
// 缓冲区数据合并
override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
buffer1.update(0, buffer1.getInt(0) + buffer2.getInt(0))
buffer1.update(1, buffer1.getInt(1) + buffer2.getInt(1))
}
// 计算
override def evaluate(buffer: Row): Any = {
buffer.getInt(0) / buffer.getInt(1)
}
}
}
强类型的姿势
object Sql01 {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("sql").setMaster("local")
val session: SparkSession = SparkSession.builder().config(conf).getOrCreate()
import session.implicits._
val dataFrame = session.read.json("datas/user.json")
dataFrame.createOrReplaceTempView("user")
// 通过udf注册自定义函数
session.udf.register("prefix", functions.udaf(new MyUAF))
session.sql("select prefix(damage) from user").show()
session.close()
}
// 作为中间缓冲区的类
case class Buf(var total: Int, var count: Int){}
// 强类型的方式 需要传 IN, BUF, OUT 三种泛型(输入类型 缓冲区类型 输出类型)
class MyUAF extends Aggregator[Int, Buf ,Int]{
// 初始值,缓冲区初始化
override def zero: Buf = Buf(0, 0)
// 根据输入的数据更新缓冲区的数据
override def reduce(b: Buf, a: Int): Buf = {
b.total += a
b.count += 1
b
}
// 合并缓冲区
override def merge(b1: Buf, b2: Buf): Buf = {
b1.total += b2.total
b1.count += b2.count
b1
}
// 计算
override def finish(reduction: Buf): Int = {
reduction.total / reduction.count
}
// 缓冲区的编码操作,自定义的类就是product
override def bufferEncoder: Encoder[Buf] = Encoders.product
// 输出的编码操作
override def outputEncoder: Encoder[Int] = Encoders.scalaInt
}
}