SparkSQL修仙学习04

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

在SparkSQL中Spark为我们提供了两个新的抽象,分别是DataFrame和DataSet。他们和RDD有什么区别呢?首先从版本的产生上来看:
RDD (Spark1.0) —> Dataframe(Spark1.3) —> Dataset(Spark1.6)
效率逐个变高
在这里插入图片描述

sparksql实操

1.SparkSession操作步骤

object Demo1 {
    def main(args: Array[String]): Unit = {
      val spark = SparkSession.builder()
        .appName("Demo1")
        .master("local[*]")
        //            .enableHiveSupport()//支持hive的特定操作
        .getOrCreate()
      //读取数据,json格式
      val pdf = spark.read.json("C:\\Users\\70201\\Desktop\\sql\\people.json")
      println("------获取表中的元数据信息-----------")
      pdf.printSchema()
      println("------获取表中的数据信息-----------")
      pdf.show()
      println("------筛选表中的个别字段-----------")
      pdf.createOrReplaceTempView("people")

      var sql=
        """
          |select
          |name,age
          |from
          |people
          |""".stripMargin
      spark.sql(sql).show()
      println("------条件查询-----------")
      sql=
        """
          |select
          |name,age
          |from
          |people
          |where name="肖楚轩"
          |""".stripMargin
      spark.sql(sql).show()
      println("------统计-----------")
      sql=
        """
          |select
          |count(*)
          |from
          |people
          |""".stripMargin
      spark.sql(sql).show()
      println("------复杂统计统计-----------")
      sql=
        """
          |select
          |province,count(name) Count,max(age) maxAge
          |from
          |people
          |group by province
          |""".stripMargin
      spark.sql(sql).show()
      spark.stop()
  }
}

2. DataFrame的构建

/**
 * SparkSQL中的编程模型主要有:DataFrame和Dataset
 * DataFrame的构建分为了两种方式
 *      基于反射的方式构建
 *      基于动态编程的方式构建
 * Dataset的构建方式是和dataframe差不多一样
 */
object Demo2 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder()
      .appName("Demo2")
      .master("local[*]")
      .getOrCreate()
    //List[person]>>List[Row]
    val rows = List(
      Person1("韩香彧", 17, 167.5),
      Person1("石云涛", 88, 147.5),
      Person1("刘炳文", 20, 170.5),
      Person1("乔钰芹", 16, 167.5)
    ).map(person => {
      Row(person.name,person.age,person.height)
    })
    //List[Row]>>java的List[Row]
    val rows1 = JavaConversions.seqAsJavaList(rows)
    val structType = StructType(
      Array(
        StructField("name", DataTypes.StringType, false),
        StructField("age", DataTypes.IntegerType, false),
        StructField("height", DataTypes.DoubleType, false)
      )
    )
    //java的List[Row]
    val df = sparkSession.createDataFrame(rows1, structType)
    df.show
    creatByBean(sparkSession)
    sparkSession.stop()
  }


  def creatByBean(spakSession:SparkSession): Unit ={
    //Applies a schema to a List of Java Beans
    val persons1 = new util.ArrayList[Person]
    persons1.add(new Person("韩香彧", 17, 167.5))
    persons1.add(new Person("石云涛", 88, 147.5))
    persons1.add(new Person("刘炳文", 20, 170.5))
    //需要java的list
    val pdf = spakSession.createDataFrame(persons1, classOf[Person])

    pdf.show()
  }
}
case class Person1(name: String, age: Int, height: Double)

3.Dataset的构建

 /**
 *         * Dataset的构建
 *         *
 *         * dataset在构造的时候需要两个条件:
 *         *  第一导入隐式转换:import spark.implicits._
 *         *  第二要求封装数据类型为Product的子类,最好就是case class
 **/
 */
object Demo3 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().master("local[*]").appName("Demo3").getOrCreate()
    //要求封装数据类型为Product的子类(int.string等和case class),最好就是case class
    val list = List(
      Student("韩香彧", 17, 167.5),
      Student("石云涛", 88, 147.5),
      Student("刘炳文", 20, 170.5),
      Student("乔钰芹", 16, 167.5)
    )
    //scala的list,隐式转换
    import sparkSession.implicits._
    val value = sparkSession.createDataset(list)
    value.show()
  }
}
case class Student(name: String, age: Int, height: Double)

4.编程模型之间的转换

/**
 * 编程模型之间的互相转换:
 *   rdd--dataframe/dataset
 *   dataframe-->rdd/dataset
 *   dataset=-->dataframe/rdd
 */
object Demo4 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().master("local[*]").appName("Demo4").getOrCreate()
    val list = List(
      Student("韩香彧", 17, 167.5),
      Student("石云涛", 88, 147.5),
      Student("刘炳文", 20, 170.5),
      Student("乔钰芹", 16, 167.5)
    )
    val value:RDD[Student] = sparkSession.sparkContext.parallelize(list)
    //隐式转换
    import sparkSession.implicits._
    println("rdd--->dataframe")
    val df = value.toDF()
    df.show()
    println("rdd--->dataSet")
    val ds = value.toDS()
    ds.show()
    println("dataframe--->rdd")
    //dataframe的数据是一个一个的Row
    val rdd = df.rdd
    rdd.foreach{case Row(name,age,height)=>{
      println(s"${name},${age},${height}")
    }}
    println("dataframe--->dataSet")
    println(
      """
        | dataframe 不能直接转化为Dataset
        | 为什么?我们前了解到dataframe中的泛型是Row,那么转化为Dataset其实就成了Dataset[Row]
        | 由于Row并不是Product的子类,并没有提供一个Encoder所以不能作为dataset的数据类型
        | 故而,不可直接转化为dataset
        |""".stripMargin)

    println("dataSet--->dataframe")
    ds.toDF().show()
    println("dataSet--->rdd")
    val rdd1 = ds.rdd
    rdd1.foreach(stu=>{
      println(s"${stu.name},${stu.age},${stu.height}")
    })
    sparkSession.stop()
  }
}

5.数据写出保存落地

//数据落地
object Demo6 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().master("local[*]").appName("Demo5").getOrCreate()
    import sparkSession.implicits._
    val ds = sparkSession.read.textFile("C:\\Users\\70201\\Desktop\\sql\\people.txt")
      .map(line=>{
        val strings = line.split(",")
        val name = strings(0).trim//去左右两边空格
        val age = strings(1).trim.toInt
        info(name,age)
      })
    /*
           数据的落地
           SaveMode:
               ErrorIfExists 默认的
               Append        追加
               Overwrite     覆盖
               Ignore        忽略,如果目录已经存在,则忽略,如果目录不存在,则执行创建
        */
    ds.write.mode(SaveMode.Ignore).save("C:\\Users\\70201\\Desktop\\test\\save")
    ds.write.mode(SaveMode.Ignore).json("C:\\Users\\70201\\Desktop\\test\\json")
//  csv导出默认是,
//    Michael,29
//    Andy,30
//    Justin,19
    //可以指定输出格式option("header","true").option("delimiter","|")
    ds.write.mode(SaveMode.Overwrite).option("header","true").option("delimiter","|").csv("C:\\Users\\70201\\Desktop\\test\\csv")
    var url="jdbc:mysql://localhost:3306/test"
    var table="info"//最好先建表,虽然此处会自动生成,但表字段的数据类型给的不是很完美varchar--Text
    val properties = new Properties()
    properties.put("user","root")
    properties.put("password","123456")
    ds.write.jdbc(url,table,properties)
    sparkSession.stop()
  }
}

6.数据加载

/**
 * SparkSQL对数据的统一加载和落地操作
 *  加载使用
 *      read.load
 *           not a Parquet file. expected magic numbe ==> 默认加载的文件格式要求是parquet,是一个二进制的列式存储格式文件,twitter公司开源到apache的
 *      option
 *          https://docs.databricks.com/data/data-sources/aws/amazon-s3-select.html#csv-specific-options
 *  落地使用
 *      write.save
 */
//数据加载
object Dmeo5 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().master("local[*]").appName("Demo5").getOrCreate()
    var df = sparkSession.read.load("C:\\Users\\70201\\Desktop\\sql\\sqldf.parquet")
    df.show()
    //对于复杂的操作,需要设置一些option选项来完成过滤或者修正
    df = sparkSession.read
        .option("header","true")
        .option("delimiter","|")//对不规范的csv要指定分隔符,让程序怎么切分成对应字段
      .csv("C:\\Users\\70201\\Desktop\\sql\\student.csv")
    df = sparkSession.read
      .csv("C:\\Users\\70201\\Desktop\\sql\\country.csv")
        .toDF("id","country","code")//对没有表头的csv可以转成df并指定字段名
    df.show()
    //orc是一种列式存储文件,式rc的升级版本呢,是facebook用来存储数据的文件格式
    df=sparkSession.read.orc("C:\\Users\\70201\\Desktop\\sql\\student.orc")
    df.show()
    df=sparkSession.read.json("C:\\Users\\70201\\Desktop\\sql\\product_info.json")
    df.show()
    import sparkSession.implicits._//隐式转换,为了解决ds的encoder
    //text文件认为只有一列数据,我们可以拆分
    val ds = sparkSession.read.textFile("C:\\Users\\70201\\Desktop\\sql\\people.txt")
        .map(line=>{
          val strings = line.split(",")
          val name = strings(0).trim//去左右两边空格
          val age = strings(1).trim.toInt
          info(name,age)
        })
    ds.show()
//    url: String,
//    table: String,
//    columnName: String,
    var url="jdbc:mysql://localhost:3306/test"
    var table="wordcounts"
    val properties = new Properties()
    //a "user" and "password" property
    properties.put("user","root")
    properties.put("password","123456")
    df=sparkSession.read.jdbc(url,table,properties)
    df.show()
    sparkSession.stop()
  }
}
case class info(name: String,age:Int)

7.sql操作hive表

打包插件

/*spark和hive整合时需要注意的地方:
*      1、为了能够让spark正常的解析hive的仓库为止,需要将hive-site.xml传递给spark,加载到spark的classpath(resources目录)中
*          一种通过直接将hive-site.xml放到spark的conf目录下面
*          另外一种就是通过程序的方式放到classpath即可(第二种)
*      2、在hive-site.xml中最重要的就是一个参数
*      <property>
*      <name>hive.metastore.warehouse.dir</name>
*      <value>/user/hive/warehouse</value>
*      </property>
*      如果没有配置这个参数,就会在当前程序的当前目录下面指定hive的warehouse,而真正的数据在hdfs里卖弄,执行的时候会找不到数据:
*          except: file:///  hdfs://
*       3、同时如果配置hadoop高可用得需要解析出hdfs的具体路径,所以也需要将hdfs-site.xml和core-site.xml也打到classpath下面
*       4、得需要将mysql的驱动包打入classpath中
*/
//1.导入打包插件,将spark-hive依赖取消,因为linux的spark有支持,省的打包时文件过大
//2.对父module打包到本地install,因为有spark_sql的相关依赖spark_common,spark_sql打包package会先去本地仓库找spark_common,找不到再去远程仓库,
//3.上传linux是带有依赖的jar
//4.编写执行脚本,要去掉local,因为程序有了,要把enableHiveSupport()打开
//5.执行
object Demo1 {
  def main(args: Array[String]): Unit = {
    if(args==null||args.length!=2){
      println("null")
      System.exit(-1)
    }
    val Array(basic,info)=args
    val sparkSession = SparkSession.builder()
      .appName("Demo1")
      .master("local[*]")
      .enableHiveSupport()//支持hive的特定操作
      .getOrCreate()

    println("1.创建数据库")
    sparkSession.sql(
      """
        |create database if not exists info
        |""".stripMargin)
    println("2.创建表teacher_basic")
    sparkSession.sql(
      """
        |create table if not exists info.teacher_basic(
        |name string,
        |age int,
        |merry boolean,
        |course int
        |)
        |row format delimited fields terminated by ','
        |""".stripMargin)
    println("3.创建表teacher_info")
    sparkSession.sql(
      """
        |create table if not exists info.teacher_info(
        |name string,
        |height int
        |)
        |row format delimited fields terminated by ','
        |""".stripMargin)
    println("4.加载数据到表teacher_basic")
    sparkSession.sql(
      s"""
        |load data inpath '${basic}' into table info.teacher_basic
        |""".stripMargin)
    println("5.加载数据到表teacher_info")
    sparkSession.sql(
      s"""
         |load data inpath '${info}' into table info.teacher_info
         |""".stripMargin)
    println("6.关联两张表")
    val df = sparkSession.sql(
      """
        |select
        |b.name,b.age,b.merry,b.course,i.height
        |from info.teacher_basic b
        |join info.teacher_info i
        |on b.name=i.name
        |""".stripMargin)
    println("7.将关联数据写入到表teacher")
    df.write.saveAsTable("info.teacher")
    println("结束!")
    sparkSession.stop()
  }
}

8.自定义UDF函数

object Demo2 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().appName("Demo2").master("local[*]").getOrCreate()
    val df = sparkSession.read.json("C:\\Users\\70201\\Desktop\\sql\\people.json")
     //3.建视图
    /*
            注册一张临时表
            global
                在整个应用范围内有效,不带的话只在当前sparkSession内有效
            replace
                如果该视图存在,则会覆盖,否则新建
         */
    df.createOrReplaceTempView("people")
    //2.注册udf函数  返回值类型int,输入类型string
    sparkSession.udf.register[Int,String]("mylength",str=>myle(str))
    //4.使用,执行之前要把之前hive.sql依赖解开,把hive-site.mxl去掉,否则会加载到hdfs找数据
    sparkSession.sql(
      """
        |select
        |name,
        |mylength(name) mylen,
        |length(name) len
        |from
        |people
        |""".stripMargin).show()
    sparkSession.stop()
  }
  //1.自定义udf函数
  def myle(str:String):Int={
    str.length
  }
}

9.自定义UDAF函数

object Demo3 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().appName("Demo3").master("local[*]").getOrCreate()
    val df = sparkSession.read.json("C:\\Users\\70201\\Desktop\\sql\\people.json")
    df.createOrReplaceTempView("student")
    sparkSession.udf.register("myavg",new AvgHeight)
    sparkSession.sql(
      """
        |select
        |round(avg(height),1) avg,
        |round(myavg(height),1) myavg
        |from
        |student
        |""".stripMargin).show()
    sparkSession.stop()
  }
}

自定义类

class AvgHeight extends UserDefinedAggregateFunction{
  /*
        该udaf输入参数的类型说明
     */
  override def inputSchema: StructType = StructType(List(
    StructField("height", DataTypes.DoubleType, false)
  ))
  /*
      为了计算聚合结果所需要的涉及到的临时变量的类型
      平均数=总数/个数,这里面涉及到了2个临时变量,总数,个数
   */
  override def bufferSchema: StructType =  StructType(List(
    StructField("sum", DataTypes.DoubleType, false),
    StructField("count", DataTypes.IntegerType, false)
  ))
  /*
      该udaf返回值的数据类型
   */
  override def dataType: DataType = DataTypes.DoubleType
  /*
      确定性,相同的输入,其返回值是确定,不会有其他可能,称之为确定性,即返回为true
      given the same input,always return the same output.
   */
  override def deterministic: Boolean = true
  //初始化
  override def initialize(buffer: MutableAggregationBuffer): Unit = {
    buffer.update(0, 0.0)
    buffer.update(1, 0)
  }

  //局部聚合,new input data from `input`,This is called once per input row.
  override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
    buffer.update(0, buffer.getDouble(0) + input.getDouble(0))
    buffer.update(1, buffer.getInt(1) + 1)
  }

  //分区间的全局聚合
  override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
    buffer1.update(0, buffer1.getDouble(0) + buffer2.getDouble(0))
    buffer1.update(1, buffer1.getInt(1) + buffer2.getInt(1))
  }

  override def evaluate(buffer: Row): Double = {
    buffer.getDouble(0) / buffer.getInt(1)
  }
}

10.开窗函数,分组求TOPN

object Demo4 {
  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().appName("Demo4").master("local[*]").getOrCreate()
    val df = sparkSession.read.json("C:\\Users\\70201\\Desktop\\sql\\people.json")
    df.createOrReplaceTempView("student")
    sparkSession.sql(
      """
        |select
        |tmp.*
        |from
        |(select
        |name,age,province,height,
        |row_number() over(partition by province order by height desc) rank
        |from
        |student) tmp
        |where tmp.rank<3
        |""".stripMargin).show()
    sparkSession.stop()
  }
}

11.数据倾斜(重点!!!)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值