Spark Sql编程实践

1 DataFrame编程

在这里插入图片描述

代码中创建DataFrame

//1.先创建SparkSession
val spark = SparkSession.builder()
  .appName("CreateDF").master("local[2]")
  .getOrCreate()
//2.通过SparkSession创建DF
val df = spark.read.json("D:/users.json")
//3.对DF做操作(sql)
//3.1 创建临时表
df.createOrReplaceTempView("user")
//3.2 查询临时表
spark.sql("select * from user").show
//4.关闭SparkSession
spark.stop()

1.1 DF转RDD

val rdd = df.rdd

2 使用DataSet进行编程

     val spark = SparkSession.builder()
      .appName("CreateDF").master("local[2]")
      .getOrCreate()
    import spark.implicits._
    val list = List(User(10, "zs"), User(20, "lisi"), User(15, "ww"))
    val ds = list.toDS()
//    ds.show()
    //在ds做sql查询
    ds.createOrReplaceTempView("user")
    spark.sql("select * from user where age>15").show
    spark.stop()

2.1 三者的区别

RDD

  1. RDD一般和spark mlib同时使用
  2. RDD不支持sparksql操作

DataFrame

  1. 与RDD和Dataset不同,DataFrame每一行的类型固定为Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值。
  2. DataFrame与DataSet一般不与 spark mlib 同时使用
  3. DataFrame与DataSet均支持 SparkSQL 的操作,比如select,groupby之类,还能注册临时表/视窗,进行 sql 语句操作
  4. DataFrame与DataSet支持一些特别方便的保存方式,比如保存成csv,可以带上表头,这样每一列的字段名一目了然。
    DataSet
  5. Dataset和DataFrame拥有完全相同的成员函数,区别只是每一行的数据类型不同。 DataFrame其实就是DataSet的一个特例。
  6. DataFrame也可以叫Dataset[Row],每一行的类型是Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的getAS方法或者共性中的第七条提到的模式匹配拿出特定字段。而Dataset中,每一行是什么类型是不一定的,在自定义了case class之后可以很自由的获得每一行的信息。

2.2 三者的共性

RDD、DataFrame、Dataset全都是 Spark 平台下的分布式弹性数据集,为处理超大型数据提供便利。
三者都有惰性机制,在进行创建、转换,如map方法时,不会立即执行,只有在遇到Action如foreach时,三者才会开始遍历运算。
三者都会根据 Spark 的内存情况自动缓存运算,这样即使数据量很大,也不用担心会内存溢出。
三者都有partition的概念
三者有许多共同的函数,如map, filter,排序等
在对 DataFrame和Dataset进行操作许多操作都需要这个包进行支持 import spark.implicits._ (spark只是为对象名 也可以是其他的名称 )
DataFrame和Dataset均可使用模式匹配获取各个字段的值和类型。

3 自定义聚合函数(弱类型的聚合函数 重点)

案例:对users中的年龄求和


object UDAFDemo {
  def main(args: Array[String]): Unit = {
    //在sql中,聚合函数如何使用
    val spark = SparkSession
      .builder()
      .appName("UDAFDemo")
      .master("local[*]")
      .getOrCreate()
     import spark.implicits._
    val df = spark.read.json("D:/users.json")
    df.createOrReplaceTempView("user")
    //注册聚合函数
    spark.udf.register("mySum",new MySum)
    spark.sql("select mySum(age)  from user  ").show
    spark.close()
  }
}
//定义mySum
class MySum extends UserDefinedAggregateFunction {
  //用来定义输入的数据类型
  override def inputSchema: StructType = StructType(StructField("ele",DoubleType)::Nil)
  //缓冲区的类型
  override def bufferSchema: StructType = StructType(StructField("ele",DoubleType)::Nil)
 //最终聚合结果的类型
  override def dataType: DataType = DoubleType
//相同的输入是否返回相同的输出
  override def deterministic: Boolean = true
  //对缓冲区初始化
  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    //在缓冲集合中初始化和
    buffer(0)=0D
  }
 //分区内聚合
  override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
    //input指的是使用聚合函数的时候,传过来的参数封装到了Row
    if(!input.isNullAt(0)){  //考虑到传递段可能是null
      val v = input.getDouble(0)
      buffer(0) = buffer.getDouble(0) + v
    }
  }
//分区间的聚合
  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
    //把buffer1和buffer2的缓冲值聚合在一起 ,然后再把值写会buffer1中
    buffer1(0) =buffer1.getDouble(0) + buffer2.getDouble(0)
  }
   //返回最终的输出值
  override def evaluate(buffer: Row): Any = buffer.getDouble(0)
}

案例2 :求年龄的平均值

object UDAFDemo {
  def main(args: Array[String]): Unit = {
    //在sql中,聚合函数如何使用
    val spark = SparkSession
      .builder()
      .appName("UDAFDemo")
      .master("local[*]")
      .getOrCreate()
     import spark.implicits._
    val df = spark.read.json("D:/users.json")
    df.createOrReplaceTempView("user")
    //注册聚合函数
    spark.udf.register("myAvg",new MyAvg)
    spark.sql("select myAvg(age)  from user  ").show
    spark.close()
  }
}
//定义mySum
class MyAvg extends UserDefinedAggregateFunction {
  //用来定义输入的数据类型
  override def inputSchema: StructType = StructType(StructField("ele",DoubleType)::Nil)
  //缓冲区的类型
  override def bufferSchema: StructType = StructType(StructField("sum",DoubleType) :: StructField("cnt",LongType)::Nil)
 //最终聚合结果的类型
  override def dataType: DataType = DoubleType
//相同的输入是否返回相同的输出
  override def deterministic: Boolean = true
  //对缓冲区初始化
  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    //在缓冲集合中初始化和
    buffer(0)=0D
    buffer(1)=0L
  }
 //分区内聚合
  override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
    //input指的是使用聚合函数的时候,传过来的参数封装到了Row
    if(!input.isNullAt(0)){  //考虑到传递段可能是null
      val v = input.getDouble(0)
      buffer(0) = buffer.getDouble(0) + v
      buffer(1) = buffer.getLong(1)+1L
    }
  }
//分区间的聚合
  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
    //把buffer1和buffer2的缓冲值聚合在一起 ,然后再把值写会buffer1中
    buffer1(0) =buffer1.getDouble(0) + buffer2.getDouble(0)
    buffer1(1) = buffer1.getLong(1) + buffer2.getLong(1)
  }
   //返回最终的输出值
  override def evaluate(buffer: Row): Any = buffer.getDouble(0) / buffer.getLong(1)
}

3.1 自定义强类型聚合函数(非重点)

case class Dog(name:String,age:Int)
//定义样例类 用来缓存结果
case class ageAvg(sum:Int,count:Int){
  def avg():Double = {
    sum.toDouble / count
  }
}
object UDAFDemo2 {
  def main(args: Array[String]): Unit = {
    //在sql中,聚合函数如何使用
    val spark = SparkSession
      .builder()
      .appName("UDAFDemo2")
      .master("local[*]")
      .getOrCreate()
    import spark.implicits._
    val ds = List(Dog("大黄",6),Dog("小黄",2),Dog("中黄",4)).toDS()
    //强类型的使用方式
    val avg = new MyAvg2().toColumn.name("avg")
    ds.select(avg)
    val result:Dataset[Double] = ds.select(avg)
    result.show()
    spark.close()
  }
}
class MyAvg2 extends Aggregator[Dog,ageAvg,Double] {
  //对缓冲区进行初始化
  override def zero: ageAvg = ageAvg(0,0)
  //分区内聚合
  override def reduce(b: ageAvg, a: Dog): ageAvg = a match {
    case Dog(name,age) => ageAvg(b.sum+age,b.count+1)   //如果是dog对象,则把年龄相加,个数+1
    case _=> b  //如果是null则原封不动的返回
  }
  //分区间的聚合
  override def merge(b1: ageAvg, b2: ageAvg): ageAvg = {
    ageAvg(b1.sum+b2.sum,b1.count+b2.count)
  }
 //返回最终的值
  override def finish(reduction: ageAvg): Double = reduction.avg()
  //对缓冲区进行编码
  override def bufferEncoder: Encoder[ageAvg] = Encoders.product  //如果是样例类或元组 就直接返回这个编码器
  //对返回值进行编码
  override def outputEncoder: Encoder[Double] = Encoders.scalaDouble
}

4 读取数据源

4.1 通用的读法

spark.read.format(“json”).load(路径)
format(“json”) 指定文件格式,如果没有则默认是parquet

4.2 专用的读法(一般用到的)

spark.read.json(“路径”) <=>spark.read.format(“json”).load(路径)

4.3 写数据到数据源

(1)通用写法:df.write.format(“json”).save(“路径”)
(2)专用写法:df.write.mode(“append”).json(“路径”)

5 JDBC

5.1 从jdbc读取数据

//通用写法
    val url = "jdbc:mysql://localhost:3306/rdd"
    val user = "root"
    val pwd = "123456"

        val df = spark.read
       .option("url",url)  //数据库地址
       .option("user",user)  //数据库用户
       .option("password",pwd)  //用户密码
       .option("dbtable","user")  //表
       .format("jdbc").load()
       df.show()
//专用写法
val props = new Properties()
props.put("user",user)
props.put("password",pwd)
 val df = spark.read
  .jdbc(url, "user", props)
    df.show()
    spark.close()

5.2 写数据到jdbc

(1)通用写法

object JDBCWrite {
  val url = "jdbc:mysql://localhost:3306/rdd"
  val user = "root"
  val pwd = "123456"
  def main(args: Array[String]): Unit = {
    val spark = SparkSession
      .builder()
      .master("local[*]")
      .appName("JDBCWrite")
      .getOrCreate()
    import spark.implicits._
    val df = spark.read.json("D:/users.json")
    //写到jdbc中
    df.write
      .format("jdbc")
      .option("url",url)
      .option("user",user)
      .option("password",pwd)
      .option("dbtable","user1")
      .mode("append")  //向已有数据的表中追加数据 或者 .mode(SaveMode.append)
      .save()
  }
}

(2)专有写法
//专有写法

val props = new Properties()
props.put("user",user)
props.put("password",pwd)
df.write
  .jdbc(url,"user2",props)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值