Spark SQL

386 阅读3分钟

1.Spark的优势

1.完全脱离了Hive的限制

2.支持查询原生的RDD

3.能够在Scala中编写SQL语句

支持简单的SQL语法检查,能够在Scala中写Hive语句访问Hive数据,并将结果取回作为RDD使用

2.Spark SQL的最佳搭档Dataframe

1.Spark SQL是Spark的核心组件之一

2.与RDD类似,Dataframe也是一个分布式数据容器,然而Dataframe更像传统数据库的二维表格,除了数据以外,还掌握数据的结构信息,即schema。同时,与Hive类似,Dataframe也支持嵌套数据类型(struct,array,map)。从API易用性的角度上看,Dataframe API提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低

3.Dataframe创建方式

1.读json文件

sqlContext.read().format("json").load("path")

sqlContext.read().json("path")

2.读取json格式的RDD

RDD的元素类型是String,但是格式必须是JSON格式

3.读取parquet文件创建DF

4.RDD

通过非json格式的RDD来创建出来一个DataFrame

1)通过反射的方式

2)动态创建schema的方式

4.一个小测试

json文件:

{"name":"zhangsan","age":20}
{"name":"lisi"}
{"name":"wangwu","age":18}
{"name":"wangwu","age":18}

代码:

package com.jcai.spark.scala

import org.apache.spark.sql.SparkSession

object SQLTest {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("sqltest").master("local").getOrCreate()
    val people = spark.read.json("./json")
    people.printSchema()
    people.show()
  }
}

编译结果:

注:DataFrame中的列会按照ascii码排序

1.查找表中年龄大于18的数据

方法1:

    val df1: Dataset[Row] = people.select("name","age").where(people.col("age").gt(18))
    df1.show()

方法2:

将DataFrame注册为临时表,t1这张表不在内存中,也不在磁盘中,相当于一个指针指向源文件,底层操作解析spark job读取源文件

    people.createOrReplaceTempView("t1")
    people.cache()
    val resultsDF = spark.sql("select * from t1 where age > 18")
    resultsDF.show()

运行结果:

注:sql查询出来的列会按指定列展示

2.DataFrame转RDD

代码:

    val rdd: RDD[Row] = people.rdd
    rdd.foreach(x=>{
      println(x)
    })

结果:

代码:

    rdd.foreach(x=>{
      println(x.get(1))
    })

结果:

代码:

    rdd.foreach(x=>{
      println(x.getAs("age"))
    })

结果:

5.通过反射的方式将普通RDD转DF

txt文件:

1,zhangsan,18
2,lisi,19
3,wangwu,20

代码:

package com.jcai.spark.scala

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

object CreatDFFromRDD1 {
  case class Person(id:Int,name:String,age:Int)
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("sqltest").master("local").getOrCreate()
    import spark.implicits._
    val lineRDD: RDD[String] = spark.sparkContext.textFile("person.txt")
    val personRDD: RDD[Person] = lineRDD.map{
      line =>
      val attributes = line.split(",")
      Person(attributes(0).toInt, attributes(1),attributes(2).trim.toInt)
    }
    val personDF: DataFrame = personRDD.toDF()
    personDF.show()
  }
}

结果:

注:

Error:(19, 8) value toDF is not a member of org.apache.spark.rdd.RDD[Person]
possible cause: maybe a semicolon is missin

原因:

1、启用隐式转换时,需要在 main 函数中自行创建 SparkSession 对象,然后使用该对象来启用隐式转换,而非在 object 对象之前启用,即import spark.implicits._放在object里面。

2、case class 类的声明需要放在 main 函数之前。

6.通过动态创建Schema方式加载DF

txt文件:

1,zhangsan,18
2,lisi,19
3,wangwu,20

代码:

package com.jcai.spark.scala

import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.types.{StringType, StructField, StructType}

object CreateDFFromRDD2 {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder().appName("sqltest").master("local").getOrCreate()
    import spark.implicits._
    val lineRDD: RDD[String] = spark.sparkContext.textFile("person.txt")
    val schemaString = "id name age"
    val fields = schemaString.split(" ")
      .map(fieldName => StructField(fieldName, StringType, nullable = true))
    val schema = StructType(fields)
    val rowRDD = lineRDD
      .map(_.split(","))
      .map(attributes => Row(attributes(0).trim,attributes(1), attributes(2).trim))
    val peopleDF = spark.createDataFrame(rowRDD, schema)

  }

}

结果:

7.读取parquent文件加载DF

代码:

package com.jcai.spark.scala

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

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

    val spark = SparkSession.builder().appName("sqltest").master("local").getOrCreate()
    import spark.implicits._
    val peopleDF: DataFrame = spark.read.json("./json")
    /**
     * 生成parquet文件
     */
    peopleDF.show()
    peopleDF.write.parquet("D:\\df.parquet")
    val parquetFileDF = spark.read.parquet("D:\\df.parquet")
    parquetFileDF.show()
  }
}

注:编译出错了,但是应该是IDEA配置问题,代码本身是正确的,先跳过,换一个存在的parquet文件 代码:

package com.jcai.spark.scala

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

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

    val spark = SparkSession.builder().appName("sqltest").master("local").getOrCreate()
    import spark.implicits._
    val parquetFileDF = spark.read.parquet("users.parquet")
    parquetFileDF.show()
  }
}

结果:

8.读取MySQL表创建DF

9.读取Hive中的数据创建DF

10.UDF

用户自定义函数

11.UDAF

用户自定义聚合函数

12.开窗函数