文章目录

  • 定义
  • Spark SQL
  • Dataset & DataFrame
  • DataFrame的纯SQL操作
  • 单行查询
  • 模糊查询
  • 排序查询
  • 分组查询
  • limit(限制返回结果条数)
  • having(分组后过滤)
  • case...when语句
  • pivot(行转列)
  • cube(多维度分组)
  • join表连接查询
  • 子查询


定义

Spark SQL

  1. 参考文档
    官方参考文档
  2. Spark SQL是Spark中一个模块,用以对结构化数据进行处理。SparkSQL在RDD之上抽象出来Dataset/Dataframe 这两个类提供了类似RDD的功能,也就意味用户可以使用map、flatMap、filter等高阶算子,同时也通过了基于列的命名查询,也就是说Dataset/DataFrame提供了两套操作数据的API,这些API可以给Saprk引擎要提供更多信息,系统可以根据这些信息对计算实现一定的优化。
    目前Spark SQL提供了两种交互方式:
    1) SQL 脚本 ,
    2) Dataset API (strong-typed类型、untyped类型操作)

Dataset & DataFrame

Dataset是一个分布式数据集,Dataset是在spark-1.6提出新的API,该API构建在RDD(strong type,使用lambda表达式)之上同时可以借助于Spark SQL对执行引擎的优点,使得使用Dateset执行一些数据的转换比直接使用RDD算子功能和性能都有所提升。因此我们可以认为Dateset就是一个加强版本的RDD。Dataset除了可以使用JVM中数组|集合对象创建之外,也可以将任意的一个RDD转换为Dataset。Python does not have the support for the Dataset API.

DataFrames 是Dataset的一种特殊情况。比如 Dataset中可以存储任意对象类型的数据作为Dataset的元素。但是Dataframe的元素只有一种类型Row类型,这种基于Row查询和传统数据库中ResultSet操作极其相似。因为Row类型的数据表示Dataframe的一个元素,类似数据库中的一行,这些行中的元素可以通过下标或者column name访问。由于Dateset是API的兼容或者支持度上不是多么的好,但是Dataframe在API层面支持的Scala、Java、R、Python支持比较全面。

DataFrame的纯SQL操作

df.createGlobalTempView()  //  对DF创建全局的临时视图,它产生的表,可以多个spark session共享,它的生命周期和spark application绑定
df.createTempView()  // 对DF创建局部的临时视图,它产生的表,仅供创建spark session使用,其它的spark session无法获取

单行查询

package com.baizhi.sql

import org.apache.spark.sql.SparkSession

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

    // 1. sparkSession是spark sql应用入口,内部封装了sparkconf和sparkContext
    val spark = SparkSession
      .builder()
      .appName("the first spark sql example")
      .master("local[*]")
      .getOrCreate()

    // 2. 创建Dataset
    val rdd = spark.sparkContext.makeRDD(List("Hello Hadoop", "Hello Scala")).flatMap(_.split(" ")).map((_, 1))
	//导入spark的隐式增强之后才能使用RDD的toDF()方法把RDD转换成DataFrame
    import spark.implicits._
    val df = rdd.toDF("word", "num")

    // 给df起了表名 如果是全局表的话,在访问的时候需要加数据库名【】
    // df.createGlobalTempView("t_user") //  对DF创建全局的临时视图,它产生的表,可以多个spark session共享,它的生命周期和spark application绑定
    df.createTempView("t_user") // 对DF创建局部的临时视图,它产生的表,仅供创建spark session使用,其它的spark session无法获取

    // 再创建一个session,请问是否能够使用全局表? 正确
    //    val newSparkSession = spark.newSession()
    //    spark.sql("select * from global_temp.t_user").show()
    //    newSparkSession.sql("select * from global_temp.t_user").show()

    // 再创建一个session,请问是否能够使用局部临时表? 错误
    val newSparkSession = spark.newSession()
    spark.sql("select * from t_user").show()
    newSparkSession.sql("select * from t_user").show()
    spark.stop()
  }
}

模糊查询

import spark.implicits._
val userDF = List((1, "zs", true, 18, 15000, 1), (2, "ls", false, 19, 15000, 1)).toDF("id", "name", "sex", "age", "salary", "dept")

userDF.createTempView("t_user")
//查询结果为名字中带有字母"z"并且年龄大于18的
spark.sql("select * from t_user where name like '%z%' and age > 18").show()

排序查询

// 排序查询
spark.sql(
    // 自动将"""引起的内容 进行字符串拼接
    """
        select
          *
        from t_user
        order by id desc
    """).show()

分组查询

spark.sql(
      """
        select
          sex,avg(salary)
            as avg_salary
        from
          t_user
        group
          by sex
      """).show()
//---------------------------------------------------------------------------

+-----+----------+
|  sex|avg_salary|
+-----+----------+
| true|   15000.0|
|false|   15000.0|
+-----+----------+

limit(限制返回结果条数)

// 分组查询  统计男和女的平均工资
spark.sql(
    """
        select
          sex,avg(salary)
            as avg_salary
        from
          t_user
        group
          by sex
        limit 1	//只返回一条查询结果
      """).show()
//---------------------------------------------------------------------------
+----+----------+
| sex|avg_salary|
+----+----------+
|true|   15000.0|
+----+----------+

having(分组后过滤)

spark.sql(
    """
            select
              sex,avg(salary)
                as avg_salary
            from
              t_user
            group
              by sex
            having
              sex = true	//分组后返回性别为true的
          """).show()
//---------------------------------------------------------------------------
+----+----------+
| sex|avg_salary|
+----+----------+
|true|   15000.0|
+----+----------+

case…when语句

.sql(
      """
        | select
        | id,name,age,
        | case sex
        | when true
        |   then '男'
        | else '女'
        | end as newsex
        | from t_employee
      """.stripMargin)
.show()

pivot(行转列)

var scoreDF = List(
  (1, "语文", 100),
  (1, "数学", 100),
  (1, "英语", 100),
  (2, "数学", 79),
  (2, "语文", 80),
  (2, "英语", 100))
  .toDF("id", "course", "score")
scoreDF.createTempView("t_course")
spark.sql(
  """
    | select * from t_course
    | pivot(max(score) for course in('数学','英语','语文'))
  """.stripMargin)
  .show()

cube(多维度分组)

所谓多维度查询就是根据查询条件的不同组合来查询,例如cube(A,B),它有四种组合情况,(A,B)、(A,null)、(null,B)、(null,null),然后根据这些组合进行分组,这就是多维度分组查询

// cube (A,B)
    //    A null
    //    null B
    //    A B
    val df2 = List(
      (110, 50, 80, 80),
      (120, 60, 95, 75),
      (120, 50, 96, 70))
      .toDF("height", "weight", "uiq", "ueq")
    df2.createTempView("tt_user")

    spark.sql(
      """
        | select
        |   height,uiq,avg(uiq)
        | from
        |   tt_user
        | group by
        |   cube(height,uiq)
      """.stripMargin).show()

//-----------------------------------------------------------------
+------+----+-----------------+
|height| uiq|         avg(uiq)|
+------+----+-----------------+
|   120|null|             95.5|
|  null|  80|             80.0|
|  null|null|90.33333333333333|
|  null|  95|             95.0|
|   120|  95|             95.0|
|   110|null|             80.0|
|   110|  80|             80.0|
|   120|  96|             96.0|
|  null|  96|             96.0|
+------+----+-----------------+

join表连接查询

val userInfoDF = spark.sparkContext.makeRDD(List((1, "zs"), (2, "ls"), (3, "ww"))).toDF("id", "name")
    val orderInfoDF = spark.sparkContext.makeRDD(List((1, "iphone", 1000, 1), (2, "mi9", 999, 1), (3, "连衣裙", 99, 2))).toDF("oid", "product", "price", "uid")
    userInfoDF.createTempView("t_user")
    orderInfoDF.createTempView("t_order")
    //连接类型:inner    left_outer    right_outer   full    cross
    spark.sql(
      """
        | select * from t_user t1
        | inner join
        | t_order t2
        | on
        | t1.id = t2.uid
      """.stripMargin).show()
//-------------------------------------------------------------
+---+----+---+-------+-----+---+
| id|name|oid|product|price|uid|
+---+----+---+-------+-----+---+
|  1|  zs|  1| iphone| 1000|  1|
|  1|  zs|  2|    mi9|  999|  1|
|  2|  ls|  3| 连衣裙|   99|  2|
+---+----+---+-------+-----+---+

子查询

以一个select查询的结果作为一张要去查询的表进行查询

// 子查询
    val df =
      List(
        (1, "zs", true, 1, 15000),
        (2, "ls", false, 2, 18000),
        (3, "ww", false, 2, 14000),
        (4, "zl", false, 1, 18000),
        (5, "win7", false, 1, 16000)
      ).toDF("id", "name", "sex", "dept", "salary")
    df.createTempView("t_employee")

    
    spark.sql(
      """
        select
          id,
          name,
          sex,
          dept
        from (select * from t_employee)
      """.stripMargin).show()

//-----------------------------------------------------
+---+----+-----+----+
| id|name|  sex|dept|
+---+----+-----+----+
|  1|  zs| true|   1|
|  2|  ls|false|   2|
|  3|  ww|false|   2|
|  4|  zl|false|   1|
|  5|win7|false|   1|
+---+----+-----+----+