Spark知识点(SQL)

Spark SQL是什么?

Spark SQL是用于处理结构化数据的一个模块

DataFrame是什么?

DataFrame类似于传统数据库中的二维表格,他于RDD的区别在,DataFrame带有模式源信息,就是二维表的每一列都带有名称和类型。RDD由于无法得知具体的一个内部结构,Spark Core只能在stage层进行简单通用的流水线优化。

在这里插入图片描述
左边的RDD,为Person类型的参数,但是Spark并不了解它的内部结构。
右边的DataFrame,提供了详细的结构信息,SparkSQL就能清楚的知道每一行每一列的名称还有数据类型。

DataSet是什么?

是 DataFrame的一个扩展,是一个强类型的。

RDD,DataFrame,DataSet 三者区别

相同:

  1. 都是分布式数据集
  2. DataFrame底层是RDD,但是DataSet不是,不过他们最后都是转换成RDD运行
  3. DataSet和DataFrame的相同点都是有数据特征、数据类型的分布式数据集(schema)

不同:

  1. RDD中的数据是没有数据类型的
  2. DataFrame中的数据是弱数据类型,不会做数据类型检查
    ( 虽然有schema规定了数据类型,但是编译时是不会报错的,运行时才会报错)
  3. DataSet中的数据类型是强数据类型
  4. RDD和DataFrame默认的序列化机制是java的序列化,可以修改为Kyro的机制
  5. DataSet使用自定义的数据编码器进行序列化和反序列化

SQL基本使用

读取 json 文件创建 DataFrame

scala> val df = spark.read.json("data/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, username: string]
//结果:
+---+--------+
|age|username|
+---+--------+
| 20|zhangsan|
+---+--------+

在这里插入图片描述
需要注意的是!

普通临时表是 Session 范围内的,如果new一个Session,就报错了,这个时候我们需要用全局临时表
在这里插入图片描述
需要注意的是,使用全局临时表,需要全路径访问

DSL语法

在这里插入图片描述
查看需要运算的列
在这里插入图片描述

三者之间关系的转换

需要注意的是
DataFrame => DataSet 的时候需要构建样例类
在这里插入图片描述
在这里插入图片描述

//创建上下文环境配置对象
 val conf: SparkConf = 
 new SparkConf().setMaster("local[*]").setAppName("SparkSQL01_Demo")
 //创建 SparkSession 对象
 val spark: SparkSession = 
 SparkSession.builder().config(conf).getOrCreate()

在我们需要涉及到转换操作的时候,需要引入转换规则

import spark.implicits._

这里的spark不是包名,而是上面SparkSession对象spark,导入spark中的类

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

用户自定义函数 UDF

最基本的UDF
这是json文件内容

{“username”:“zhangsan”,“age”:30}
{“username”:“lisi”,“age”:40}
{“username”:“wangwu”,“age”:50}

在这里插入图片描述

在这里插入图片描述
需求,我想要在username前面每个数据加上一个前缀
prefixName是自定义的一个函数名

spark.udf.register("prefixName",
(name:String) => {
"Name:"+name
})

使用spark.udf.register(),可以实现。
传一个String类型,然后用“Name”与字段数据拼接

UDAF

需求我要求得年龄的平均值

  1. 写一个方法,继承类UserDefinedAggregateFunction
  2. 重写方法
package com.atguigu.bigdata.spark.sql

import org.apache.spark.SparkConf
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, LongType, StructField, StructType}
import org.apache.spark.sql.{DataFrame, Row, SparkSession}

object Spark01_SparkSQL_UDAF1 {
  def main(args: Array[String]): Unit = {

    //    todo 创建SparkSQL运行环境
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
    val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()


    //    todo 执行逻辑操作
    val df: DataFrame = spark.read.json("datas/user.json")
    df.createOrReplaceTempView("user")
    spark.udf.register("ageAvg", new MyAvgUDAF())
    spark.sql("select ageAvg(age) from user").show()
    //    todo 关闭环境

    spark.close()

  }

  class MyAvgUDAF extends UserDefinedAggregateFunction {
    //    inputSchema输入数据的结构
    //输入的数据是年龄,我们设定为Long类型,而这个LongType是StructField里面的类
    //这个方法需要我们返回一个StructType类型,那我们就给他返回呗 StructType()
    /*case class StructType(fields: Array[StructField]) extends DataType with 
    Seq[StructField] */
    //这里StructType需要传一个Array类型,我们就给他传一个Array类型
    //Array里面需要一个StructField类型,那我们就给他传一个StructField类型
    /*case class StructField(
    name: String,
    dataType: DataType,		//因为后面的都有初始值,我们只需要传递两个参数,name和dataType
    nullable: Boolean = true,
    metadata: Metadata = Metadata.empty)*/
    override def inputSchema: StructType = {
      StructType(
        Array(
          StructField("age", LongType)
        )
      )
    }

    //    缓冲区的数据结构
    // 缓冲区里面应该有两个数据,一个就是total累加的值,一个是count次数的值
    override def bufferSchema: StructType = {
      StructType(Array(
        StructField("total", LongType),
        StructField("count", LongType)
      ))
    }

    //函数计算结果的数据类型 输出的值
    override def dataType: DataType = LongType

    //函数的稳定性
    override def deterministic: Boolean = true

    //缓冲区初始化,初始化就应该都是0
    override def initialize(buffer: MutableAggregationBuffer): Unit = {
      buffer.update(0, 0L)
      buffer.update(1, 0L)
    }

    //    根据输入的值更新缓冲区,我拿到buffer前一个数据,和加载进来的数据
    override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
      buffer.update(0, buffer.getLong(0) + input.getLong(0))
      buffer.update(1, buffer.getLong(1) + 1)
    }

    //缓冲区数据合并
    override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
      buffer1.update(0, buffer1.getLong(0) + buffer2.getLong(0))
      buffer1.update(1, buffer1.getLong(1) + buffer2.getLong(1))
    }

    //计算平均值
    override def evaluate(buffer: Row): Any = {
      buffer.getLong(0) / buffer.getLong(1)
    }
  }
}

在这里插入图片描述
计算的逻辑就是,把age累加,count数量加1,然后再把缓冲区的数据相加,再把累加结果和个数一除就能得到最终结果。

优化

package com.atguigu.bigdata.spark.sql

import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.expressions.{Aggregator, MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, LongType, StructField, StructType}
import org.apache.spark.sql.{DataFrame, Dataset, Encoder, Encoders, Row, SparkSession, functions}

object Spark01_SparkSQL_UDAF {
  def main(args: Array[String]): Unit = {

    //    todo 创建SparkSQL运行环境
    val sparkConf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
    val spark: SparkSession = SparkSession.builder().config(sparkConf).getOrCreate()
    import spark.implicits._


    //    todo 执行逻辑操作
    val df: DataFrame = spark.read.json("datas/user.json")
    df.createOrReplaceTempView("user")
    spark.udf.register("ageAvg", functions.udaf(new MyAvgUDAF()))
    spark.sql("select ageAvg(age) from user").show()
    //    todo 关闭环境

    spark.close()

  }

  //in 输入的数据类型
  //buff 缓冲区的数据类型
  //out 输出的数据类型
  case class Buff(var total: Long, var count: Long)

  class MyAvgUDAF extends Aggregator[Long, Buff, Long] {
    //    z&zero 初始值,0值
    //    缓冲区的初始化
    override def zero: Buff = {
      Buff(0L, 0L)
    }

    //更具输入的数据更新缓冲区
    override def reduce(buff: Buff, in: Long): Buff = {
      buff.total = buff.total + in
      buff.count = buff.count + 1
      buff
    }

    //合并缓冲区
    override def merge(buff1: Buff, buff2: Buff): Buff = {
      buff1.total = buff1.total + buff2.total
      buff1.count = buff1.count + buff2.count
      buff1
    }

    //    计算结果
    override def finish(buff: Buff): Long = {
      buff.total / buff.count
    }

    //  缓冲区的编码操作
    override def bufferEncoder: Encoder[Buff] = Encoders.product

    //输出的编码操作
    override def outputEncoder: Encoder[Long] = Encoders.scalaLong
  }

}

Spark3.0后不推荐使用UserDefinedAggregateFunction来实现用户自定义弱类型聚合函数。可以采用强类型聚合函数Aggregate

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值