SparkSQL代码整理(完整版)

24 篇文章 9 订阅

对比于之前所学的MySQL和hive以及Spark,SparkSQL存在不可替代的高性能,SparkSQL在很多公司也是进行使用的,所以就是针对于代码进行一个整理的过程,留下一个熟悉代码的过程.Spark内核是真的难以理解,也就写写SQL比较简单.

  • 针对于Spark环境的理解(资料的2.6节)

构建之前需要在xml文件之中添加依赖.(注意:这里的依赖的顺序是不可以颠倒的,否则会出现报错的情况,具体原因自己理解)


    <dependencies>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-sql_2.12</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-hive_2.12</artifactId>
            <version>3.0.0</version>
        </dependency>

    </dependencies>

写一段json格式的代码进行案例操作:(名称为user.json),实际的文件会出现标红的问题,并不是出现了错误,这里应当注意.

{"id":1001,"age":20,"name":"qiaofeng"}
{"id":1002,"age":19,"name":"xuzhu"}
{"id":1003,"age":18,"name":"duanyu"}

为什么json格式是上面的样式???

SparkSQL读取文件采用的是Hadoop的方式,按行进行读取,需要的每一行数据都是符合json格式.

测试代码段如下所示:


import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}

object SparkSQL01 {
  def main(args: Array[String]): Unit = {
    /* 方式一:进行builder构建起进行设计者模式
    val sparkSession: SparkSession = SparkSession.builder()
      .master("local[2]")
      .appName("SparkSQL")
      .getOrCreate()
      */
    //方式二:使用普通模式进行相应的调用过程
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val sparkSession: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    //import sparkSession.implicits._

    //TODO: 1.准备json格式的文件
    //json文件要求:文件的内容是符合json文件的格式
    val dataFrame: DataFrame = sparkSession.read.json("user.json")

    //TODO: 2.将DataFrame转换为表进行SQL访问
    dataFrame.createOrReplaceTempView("user")

    //在程序之中写入sql是不方便的,因此是使用多个引号进行一个操作的过程
    sparkSession.sql(
      """
        |select
        |*
        |from
        |user
      """.stripMargin
    ).show

    sparkSession.stop()

  }
}


  • DSL&SQL

import org.apache.spark.SparkConf
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}

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

    //方式二:使用普通模式进行相应的调用过程
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    //import sparkSession.implicits._

    //TODO: 1.准备json格式的文件
    //json文件要求:文件的内容是符合json文件的格式
    val df: DataFrame = spark.read.json("user.json")
    
    //下面进行隐式转换之中spark不是包名,是SparkSession的对象名称
    // 使用的scala中的对象导入语法
    //这个对象必须是val类型的,var类型是不可以的
    import spark.implicits._//这里的引入使得在进行相应的操作的时候不进行标红[这一行的代码必须是在这里的]

    //TODO: 2.将DataFrame转换为表进行SQL访问
    //TODO 采用另外一种方式进行读取
   // df.select("id").show//注意这里必须是存在的一列,讲其中的一列进行读取
    df.select($"age"+1).show//上面的隐式转换操作进行注意的地方就是这里

    spark.stop()

  }
}



  • 数据模型之间的转换(这个操作可以在linux端之间进行操作,下面是在IDEA端进行操作的过程)

数据模型的转换表是如下所示:


import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}

object SparkSQL01 {
  def main(args: Array[String]): Unit = {
    
    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._//不管是以后有没有用到都是要进行一个添加的过程

    //RDD  => DataFrame
    val rdd = spark.sparkContext.makeRDD(List(
      (1,"zhangsan",30),
      (2,"lisi",40),
      (3,"wangwu",50),
    ))
    //将DF转换为DS的时候,设定类型时,类型应该是与该结构的字段想匹配的
    //讲DF转换为DS的时候,类型的属性可以比字段数少
    val df: DataFrame = rdd.toDF("id","name","age")

    //DataFrame => Dataset
    val ds: Dataset[User] = df.as[User]

    //Dataset => DataFrame
    val df1: DataFrame = ds.toDF()//toDF之中是可以传入相应的参数,只是将上面的列名进行一个转换的过程,动态转换

    //DataFrame => RDD
    val rdd1: RDD[Row] = df1.rdd

    //RDD => Dataset
    val ds2: Dataset[User] = rdd.map {
      case (id, name, age) => {
        User(id, name, age)
      }
    }.toDS()

    //Dataset => RDD
    val rdd2: RDD[User] = ds2.rdd

    spark.stop()

  }
  case class User(id:Int,name: String,age:Int)//样例类
  //这里的样例类的顺序如果要是进行改变的话,实际是不改变的 val df: DataFrame = rdd.toDF("id","name","age")已经定义好了
  //如果要是这里的定义跟前面是不一样的,将出现错误
  //将里面的定义删除掉一个也是可以出现正确的结果的,只是展示的时候是正确的
  
}



  • DataFrame和DataSet的区别

 

可以见到,DataFrame和DataSet的颜色是不一样的,但是DataFrame的颜色和String是一样的,点进源码之中可以看到DataFrame就是DataSet的一个别名.

DataFrame就是DataSet的一个特例.

  • UDF函数(资料的2.7节)

UDF函数的含义:在SQL之中使用自定义的函数,完成逻辑的实现


import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}

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

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._//不管是以后有没有用到都是要进行一个添加的过程

    val df: DataFrame = spark.read.json("user.json")
    df.createOrReplaceTempView("user")

    //TODO UDF:进行一个逻辑的自定义的过程
    spark.udf.register("prefixName",(name:String)=>{
      "Name:" + name
    })

    

    //注意这里sql之中的想加是进行数字的相加并不是进行一个字符串的加
    //mysql => concat(s1,s2,s3)
    //oracle => s1 // s2 //s3
    spark.sql(
      """
        |select
        |id,age,prefixName(name)
        |from
        |user
      """.stripMargin
    )
    spark.stop()

  }
  case class User(id:Int,name: String,age:Int)//样例类
  //这里的样例类的顺序如果要是进行改变的话,实际是不改变的 val df: DataFrame = rdd.toDF("id","name","age")已经定义好了
  //如果要是这里的定义跟前面是不一样的,将出现错误
  //将里面的定义删除掉一个也是可以出现正确的结果的,只是展示的时候是正确的

}
  • UDAF函数

这里的A是代表聚合的含义.


import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, IntegerType, StructField, StructType}
import org.apache.spark.sql._

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

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._//不管是以后有没有用到都是要进行一个添加的过程

    val df: DataFrame = spark.read.json("user.json")
    df.createOrReplaceTempView("user")

    //TODO UDAF:使用一个聚合逻辑进行处理,这里相应的方法见后面的逻辑
    spark.udf.register("avgAge",new AvgAgeUDAF)
    
    //注意这里sql之中的想加是进行数字的相加并不是进行一个字符串的加
    //mysql => concat(s1,s2,s3)
    //oracle => s1 // s2 //s3
    spark.sql(
      """
        |select
        |avgAge(age)
        |from
        |user
      """.stripMargin
    )
    spark.stop()

  }
  
  //TODO 自定义聚合函数
  //1.继承类
  //2.重写方法
  class AvgAgeUDAF extends UserDefinedAggregateFunction{
    //TODO 输入的数据结构
    override def inputSchema: StructType = {//样例类
      StructType(
        Array(
          StructField("age",IntegerType)
        )
      )
    }
    //TODO 在上面的数据之中buffer之中的是缓冲区之中的东西 120 数量3
    //TODO 缓冲区之中的数据结构
    override def bufferSchema: StructType = {//样例类
      StructType(
        Array(
          StructField("total",IntegerType),
          StructField("count",IntegerType)
        )
      )
    }
    
    //TODO 输出的数据结构
    override def dataType: DataType = IntegerType

    //TODO 稳定性
    override def deterministic: Boolean = true

    //TODO 缓冲区的初始化
    override def initialize(buffer: MutableAggregationBuffer): Unit = {
      buffer.update(0,0)
      buffer.update(1,0)
    }

    //TODO 更新:使用输入的值进行更新缓冲区
    override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
      buffer.update(0,buffer.getInt(0)+input.getInt(0))//旧的值和新的值的共同作用
      buffer.update(0,buffer.getInt(1)+1)
    }

    //TODO 将多个缓冲区的书进行合并
    override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
      buffer1.update(0,buffer1.getInt(0) + buffer2.getInt(0))//旧的值和新的值的共同作用
      buffer1.update(0,buffer1.getInt(1) + buffer2.getInt(1))
    }
    
    //TODO 计算结果
    override def evaluate(buffer: Row): Any = {
      buffer.getInt(0) / buffer.getInt(1)
    }
  }

}



  • UDAF函数的实现思路

上述代码的RDD实现过程


import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql._
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, IntegerType, StructField, StructType}

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

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate() //不管是以后有没有用到都是要进行一个添加的过程

    val rdd: RDD[(Int, String, Int)] = spark.sparkContext.makeRDD(List(
      (1, "张三", 30),
      (2, "lisi", 40),
      (3, "wangwu", 50)
    ))
    val rdd1: RDD[Int] = rdd.map(_._3)

    val avg = rdd1.sum / rdd1.count()

    println(avg)
    spark.stop()
  }
}



为何在实际的使用过程之中,我们不采用上述的代码,因为实际应用的时候,上面的rdd重复执行了,并且sum和count是行动算子,就会有两个job,效率很浪费.

解决上述的方式:方式一


import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql._
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, IntegerType, StructField, StructType}

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

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate() //不管是以后有没有用到都是要进行一个添加的过程

    val rdd: RDD[(Int, String, Int)] = spark.sparkContext.makeRDD(List(
      (1, "张三", 30),
      (2, "lisi", 40),
      (3, "wangwu", 50)
    ))

    //(30,1)
    //(40,2)
    //(50,1)
    val tuple: (Int, Int) = rdd.map {
      case (id, name, age) => {
        (age, 1)
      }
    }.reduce(
      (t1, t2) => {
        (t1._1 + t2._1, t1._2 + t2._2)
      }
    )

    val jieguo = tuple._1 / tuple._2
   println((Int)(jieguo))

    spark.stop()
  }
}



方式二:reudceByKey(独有的优势)


import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.sql._
import org.apache.spark.sql.expressions.{MutableAggregationBuffer, UserDefinedAggregateFunction}
import org.apache.spark.sql.types.{DataType, IntegerType, StructField, StructType}

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

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate() //不管是以后有没有用到都是要进行一个添加的过程

    val rdd: RDD[(Int, String, Int)] = spark.sparkContext.makeRDD(List(
      (1, "张三", 30),
      (2, "lisi", 40),
      (3, "wangwu", 50)
    ))

    //(30,1)
    //(40,2)
    //(50,1)
    val result: RDD[(Int, (Int, Int))] = rdd.map {
      case (id, name, age) => {
        (1, (age, 1))
      }
    }.reduceByKey(
      (t1, t2) => {
        (t1._1 + t2._1, t1._2 + t2._2)
      }
    )

    val avg = result.map{
      case(one,(total,count))=> {
        total / count
      }
    }.collect

    println(avg)

    spark.stop()
  }
}



通过上面的代码更好的理解缓冲区的概念.

  • UDAF函数-强类型操作
UserDefinedAggregateFunction是弱类型的,
UserDefinedAggregateFunction是强类型的,3.0.0之后的可以使用

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, IntegerType, StructField, StructType}
import org.apache.spark.sql._

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

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._//不管是以后有没有用到都是要进行一个添加的过程

    val df: DataFrame = spark.read.json("user.json")
    df.createOrReplaceTempView("user")

    //TODO UDAF:注意这个地方的写法就是与之前的写法是不一样的
    spark.udf.register("avgAge",functions.udaf(new AvgAgeUDAF))

    //注意这里sql之中的想加是进行数字的相加并不是进行一个字符串的加
    //mysql => concat(s1,s2,s3)
    //oracle => s1 // s2 //s3
    spark.sql(
      """
        |select
        |avgAge(age)
        |from
        |user
      """.stripMargin
    )
    spark.stop()

  }

  case class AvgBuffer(var total:Int,var count:Int)
  //TODO 自定义聚合函数(强类型)
  //1.继承类[-in,buf,out]
  //2.定义泛型
  //IN
  //BUF
  //OUT
  class AvgAgeUDAF extends Aggregator[Int,AvgBuffer,Int]{//[]之中是相应的类型
    //缓冲区的初始化
    override def zero: AvgBuffer = {
      AvgBuffer(0,0)
    }

    //将输入的值聚合到缓冲区
    override def reduce(b: AvgBuffer, a: Int): AvgBuffer =
    {
      b.total += a
      b.count += 1
      b
    }

    //缓冲区的合并
    override def merge(b1: AvgBuffer, b2: AvgBuffer): AvgBuffer = {
      b1.total += b2.total
      b1.count += b2.count
      b1
    }

    //计算结果
    override def finish(b: AvgBuffer): Int = {
      b.total/b.count
    }

    //缓冲区的类型编码,固定写法(自定义的类型)
    override def bufferEncoder: Encoder[AvgBuffer] = Encoders.product //当这里的类型是自己定义的时候,固定的写法就是这个样子

    override def outputEncoder: Encoder[Int] = Encoders.scalaInt
  }

}



强类型与弱类型的比较,其更加直观,顺序更好理解.

  • 旧版本的强类型的使用

有的公司仍然是使用的旧版的Aggregete,是不支持SQL的,为了解决这个问题,代码是如下所示:(

注意:在读取文件的时候,数字就不再是Int类型,而是相应的Long类型)


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, IntegerType, StructField, StructType}
import org.apache.spark.sql._

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

    val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("SparkSQl")
    val spark: SparkSession = SparkSession.builder().config(conf).getOrCreate()
    import spark.implicits._//不管是以后有没有用到都是要进行一个添加的过程

    val df: DataFrame = spark.read.json("user.json")
    df.createOrReplaceTempView("user")

    //TODO UDAF:Aggregator类型在早起的版本之中是不能够应用SQL
    //          将一行的数据作为整体(对象)进行聚合操作

    //SQL是弱类型
    //DSL是强类型
    val ds: Dataset[User] = df.as[User]
    //将强类型的UDAF函数作为查询zaiDSL语句之中进行洗那个赢得执行
    val udaf = new AvgAgeUDAF
    val column: Any = udaf.toColumn
    ds.select().show()

    spark.stop()

  }

  case class AvgBuffer(var total:Long,var count:Long)
  case class User(id:Long,name:String,age:Long)
  //TODO 自定义聚合函数(强类型)
  //1.继承类[-in,buf,out]
  //2.定义泛型
  //IN:User
  //BUF
  //OUT
  class AvgAgeUDAF extends Aggregator[User,AvgBuffer,Long]{//[]之中是相应的类型
    //缓冲区的初始化
    override def zero: AvgBuffer = {
      AvgBuffer(0,0)
    }

    //将输入的值聚合到缓冲区
    override def reduce(b: AvgBuffer, user: User): AvgBuffer =
    {
      b.total += user.age
      b.count += 1
      b
    }

    //缓冲区的合并
    override def merge(b1: AvgBuffer, b2: AvgBuffer): AvgBuffer = {
      b1.total += b2.total
      b1.count += b2.count
      b1
    }

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

    //缓冲区的类型编码,固定写法(自定义的类型)
    override def bufferEncoder: Encoder[AvgBuffer] = Encoders.product //当这里的类型是自己定义的时候,固定的写法就是这个样子

    override def outputEncoder: Encoder[Long] = Encoders.scalaLong
  }

}



数据读取与保存-通用方法

读取

首先是进入相应的spark模式下面

<<spark

我们上面读取的是.json文件,发现是会出现报错的现象.因为spark默认读取的文件是parquet文件之中.

 读取一个相应的parquet文件,如下所示:

保存数据

如果要是想要读取的文件是json文件的格式,需要加入一个format

另一种方式

保存

保存的过程和读取的过程是相同的,

这里的文件保存的路径是/opt/module/spark-local

操作JSON&CSV

读取json格式的文件

 SaveMode模式,就是原始的文件是已经存在的,使用其他的方式进行改进.

CSV文件的读取

SparkSQL与MySQL联合操作

首先配置依赖信息

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.27</version>
</dependency>

写出相应的操作sql语句

package com.atguigu.spark.sql

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

object SparkSQL04_Read {
  def main(args: Array[String]): Unit = {
    //创建SparkSession对象
    val spark: SparkSession = SparkSession.builder()
      .master("local[2]")
      .appName("SparkSQL")
      .getOrCreate()
  

    import spark.implicits._
    
   //读取数据
    val df =
      spark.read.
        format("jdbc")
      .option("url", "jdbc:mysql://linux1:3306/spark-sql")
      .option("driver", "com.mysql.jdbc.Driver")
      .option("user", "root")
      .option("password", "123123")
      .option("dbtable", "user")
      .load()

  //保存数据
  df.write.format("jdbc")
    .option("url", "jdbc:mysql://linux1:3306/spark-sql")
    .option("driver", "com.mysql.jdbc.Driver")
    .option("user", "root")
    .option("password", "123123")
    .option("dbtable", "user1") .mode (SaveMode.Append)
   


    //释放资源
    spark.stop()


    spark.close()
  }

}

操作内置hive

创建临时视图

创建长时视图,可以直接访问(首先创建好一个id.txt)

 >spark.sql("select * from atguigu").show

操作外置hive

步骤:①首先将hive之中的hive-site.xml文件复制到spark-local之中的conf/文件夹之中;②将MySQL驱动复制到conf之中;③:quit退出,重新启动

代码操作外置Hive

①首先将配置pom环境

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-hive_2.12</artifactId>
    <version>3.0.0</version>
</dependency>

<dependency>
    <groupId>org.apache.hive</groupId>
    <artifactId>hive-exec</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.27</version>
</dependency>

②拷贝Hive-site.xml文件到classpath之下

③配置启用hive的支持

④增加依赖关系(MySQL)和第一步一样

package com.atguigu.spark.sql

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

object SparkSQL06_Hive {
  def main(args: Array[String]): Unit = {
    val sparkSession: SparkSession = SparkSession.builder()
      .master("local[2]")
      .enableHiveSupport()//启用hive的支持
      .appName("SparkSQL")
      .getOrCreate()
    import sparkSession.implicits._


    //读取Hive数据
    sparkSession.sql("select * from test.city_info")

    sparkSession.close()
  }

}

beenline操作hive

就是展示效果是比较好的,操作方式是操不多的.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值