Spark SQL 快速入门系列(三)DataSet 和 DataFrame

Dataset 的作用和常见操作

目标

1,理解 Dataset 是什么
2,理解 Dataset 的特性

Dataset 是什么?

@Test
  def dataset1(): Unit ={
    //1.创建SparkSession.Builder
    val spark = new SparkSession.Builder()
        .master("local[6]")
        .appName(this.getClass.getSimpleName)
        .getOrCreate()

    //2.导入隐式转换
    import spark.implicits._
    //3.演示
    val sourceRDD: RDD[Person] = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), (Person("lisi", 15))))
    val dataSet = sourceRDD.toDS() //toDS 必须导入隐式转换


    //dataSet 支持RDD (强类型)  API
    dataSet.filter( x => x.age > 10).show()

    //dataSet 支持弱类型 API
    dataSet.filter( 'age> 10 ).show()
    dataSet.filter( $"age" > 10).show()

    //dataSet 支持 直接写 SQL 表达式
    dataSet.filter("age > 10").show()
  }

问题1: People 是什么?

People 是一个强类型的类

问题2: 这个 Dataset 中是结构化的数据吗?

非常明显是的, 因为 People 对象中有结构信息, 例如字段名和字段类型

问题3: 这个 Dataset 能够使用类似 SQL 这样声明式结构化查询语句的形式来查询吗?

当然可以, 已经演示过了

问题4: Dataset 是什么?

Dataset 是一个强类型, 并且类型安全的数据容器, 并且提供了结构化查询 API 和类似 RDD 一样的命令式 API

即使使用 Dataset 的命令式 API, 执行计划也依然会被优化

Dataset 具有 RDD 的方便, 同时也具有 DataFrame 的性能优势, 并且 Dataset 还是强类型的, 能做到类型安全.

 @Test
  def dataset2(): Unit ={

    //TODO 屏蔽日志信息
    Logger.getLogger("org").setLevel(Level.ERROR)

    //1.创建SparkSession.Builder
    val spark = new SparkSession.Builder()
      .master("local[6]")
      .appName(this.getClass.getSimpleName)
      .getOrCreate()

    //2.导入隐式转换
    import spark.implicits._
    //3.演示
    val sourceRDD: RDD[Person] = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), (Person("lisi", 15))))
    val dataSet = sourceRDD.toDS() //toDS 必须导入隐式转换


   //优化
    dataSet.explain(true)
    //无论DataSet 放置的是什么类型对象,最终执行计划中的RDD 上都是InteranlRow
    val executionRDD: RDD[InternalRow] = dataSet.queryExecution.toRdd

  }

Dataset 的底层是什么?

Dataset 最底层处理的是对象的序列化形式, 通过查看 Dataset 生成的物理执行计划, 也就是最终所处理的 RDD, 就可以判定 Dataset 底层处理的是什么形式的数据

  @Test
  def dataset3(): Unit ={

    //TODO 屏蔽日志信息
    Logger.getLogger("org").setLevel(Level.ERROR)

    //1.创建SparkSession.Builder
    val spark = new SparkSession.Builder()
      .master("local[6]")
      .appName(this.getClass.getSimpleName)
      .getOrCreate()

    //2.导入隐式转换
    import spark.implicits._

    //3.演示
//    val sourceRDD: RDD[Person] = spark.sparkContext.parallelize(Seq(Person("zhangsan", 10), (Person("lisi", 15))))
//    val dataSet = sourceRDD.toDS() //toDS 必须导入隐式转换
      val dataSet: Dataset[Person] = spark.createDataset(Seq(Person("zhangsan", 10), Person("lisi", 15)))

    //优化
    dataSet.explain(true)
    //无论DataSet 放置的是什么类型对象,最终执行计划中的RDD 上都是InternalRow
    //直接获取到已经分析和解析过的DataSet的执行计划,从中拿到RDD
    val executionRDD: RDD[InternalRow] = dataSet.queryExecution.toRdd


    //通过将DataSet 底层的RDD[InternalRow] 通过Decoder 转成了DataSet一样的类型RDD
    val typeRDD: RDD[Person] = dataSet.rdd

    println(executionRDD.toDebugString)
    println("-------------------------")
    println(typeRDD.toDebugString)

  }

dataset.queryExecution.toRdd 这个 API 可以看到 Dataset 底层执行的 RDD, 这个 RDD 中的范型是 InternalRow, InternalRow 又称之为 Catalyst Row, 是 Dataset 底层的数据结构, 也就是说, 无论 Dataset 的范型是什么, 无论是 Dataset[Person] 还是其它的, 其最底层进行处理的数据结构都是 InternalRow

所以, Dataset 的范型对象在执行之前, 需要通过 Encoder 转换为 InternalRow, 在输入之前, 需要把 InternalRow 通过 Decoder 转换为范型对象

在这里插入图片描述
可以获取 Dataset 对应的 RDD 表示

在 Dataset 中, 可以使用一个属性 rdd 来得到它的 RDD 表示, 例如 Dataset[T] → RDD[T]

val dataset: Dataset[People] = spark.createDataset(Seq(People("zhangsan", 9), People("lisi", 15)))

/*
(2) MapPartitionsRDD[3] at rdd at Testing.scala:159 []
 |  MapPartitionsRDD[2] at rdd at Testing.scala:159 []
 |  MapPartitionsRDD[1] at rdd at Testing.scala:159 []
 |  ParallelCollectionRDD[0] at rdd at Testing.scala:159 []
 */

//(1)
println(dataset.rdd.toDebugString) // 这段代码的执行计划为什么多了两个步骤?

/*
(2) MapPartitionsRDD[5] at toRdd at Testing.scala:160 []
 |  ParallelCollectionRDD[4] at toRdd at Testing.scala:160 []
 */

//(2)
println(dataset.queryExecution.toRdd.toDebugString)

(1)使用 Dataset.rdd 将 Dataset 转为 RDD 的形式
(2)Dataset 的执行计划底层的 RDD

可以看到 (1) 对比 (2) 对了两个步骤, 这两个步骤的本质就是将 Dataset 底层的 InternalRow 转为 RDD 中的对象形式, 这个操作还是会有点重的, 所以慎重使用 rdd 属性来转换 Dataset 为 RDD

总结

1,Dataset 是一个新的 Spark 组件, 其底层还是 RDD
2,Dataset 提供了访问对象中某个特定字段的能力, 不用像 RDD 一样每次都要针对整个对象做操作
3,Dataset 和 RDD 不同, 如果想把 Dataset[T] 转为 RDD[T], 则需要对 Dataset 底层的 InternalRow 做转换, 是一个比价重量级的操作

DataFrame 的作用和常见操作

目标

1,理解 DataFrame 是什么
2,理解 DataFrame 的常见操作

DataFrame 是什么?

DataFrame 是 SparkSQL 中一个表示关系型数据库中 表 的函数式抽象, 其作用是让 Spark 处理大规模结构化数据的时候更加容易. 一般 DataFrame 可以处理结构化的数据, 或者是半结构化的数据, 因为这两类数据中都可以获取到 Schema 信息. 也就是说 DataFrame 中有 Schema 信息, 可以像操作表一样操作 DataFrame.

在这里插入图片描述
DataFrame 由两部分构成, 一是 row 的集合, 每个 row 对象表示一个行, 二是描述 DataFrame 结构的 Schema.

在这里插入图片描述

DataFrame 支持 SQL 中常见的操作, 例如: select, filter, join, group, sort, join 等

case class Person(name:String,age:Int)
val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val peopleDF: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()

/*
+---+-----+
|age|count|
+---+-----+
| 15|    2|
+---+-----+
 */
peopleDF.groupBy('age)
  .count()
  .show()
case class Person(name:String,age:Int)
 @Test
  def dataFrame1(): Unit ={
    Logger.getLogger("org").setLevel(Level.ERROR)
    //创建SparkSession
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[6]")
      .getOrCreate()

    //创建DataFrame
    import spark.implicits._
    val dataFrame: DataFrame = Seq(Person("zhangsan", 10), Person("lisi", 15)).toDF

    //看看新花样
    dataFrame.where('age > 10)
      .select('name,'age)
      .show()
  }
/*
+----+---+
|name|age|
+----+---+
|lisi| 15|
+----+---+
*/

通过隐式转换创建 DataFrame

case class Person(name:String,age:Int)
 @Test
  def dataFrame2(): Unit = {
    Logger.getLogger("org").setLevel(Level.ERROR)
    //创建SparkSession
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[6]")
      .getOrCreate()


    //创建隐式转换
    import spark.implicits._

    val personList: Seq[Person] = Seq(Person("zhangsan", 15), Person("lisi", 20))

    /**
     * 创建的DataFrame 的三种方式
     * 1,toDF
     * 2,createDataFrame
     * 3,DataFrameReader
     */

    //toDF
    val df1 = personList.toDF()
    val df2 = spark.sparkContext.parallelize(personList).toDF()

    //createDataFrame
    val df3 = spark.createDataFrame(personList)

    //DataFrameReader(read)
    val df4 = spark.read.csv("input\\BeijingPM20100101_20151231_noheader.csv")

    df4.show()
  }

在这里插入图片描述
根据源码可以知道, toDF 方法可以在 RDD 和 Seq 中使用

通过集合创建 DataFrame 的时候, 集合中不仅可以包含样例类, 也可以只有普通数据类型, 后通过指定列名来创建

val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val df1: DataFrame = Seq("nihao", "hello").toDF("text")

/*
+-----+
| text|
+-----+
|nihao|
|hello|
+-----+
 */
df1.show()

val df2: DataFrame = Seq(("a", 1), ("b", 1)).toDF("word", "count")

/*
+----+-----+
|word|count|
+----+-----+
|   a|    1|
|   b|    1|
+----+-----+
 */
df2.show()

通过外部集合创建 DataFrame

val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

val df = spark.read
  .option("header", true)
  .csv("input\\BeijingPM20100101_20151231.csv")
df.show(10)
df.printSchema()

不仅可以从 csv 文件创建 DataFrame, 还可以从 Table, JSON, Parquet 等中创建 DataFrame, 后续会有单独的章节来介绍

案例

在 DataFrame 上可以使用的常规操作

需求: 查看每个月的统计数量

数据准备:准备100条数据
命名为:(BeijingPM20100101_20151231.csv)

No,year,month,day,hour,season,PM_Dongsi,PM_Dongsihuan,PM_Nongzhanguan,PM_US_Post,DEWP,HUMI,PRES,TEMP,cbwd,Iws,precipitation,Iprec
1,2010,1,1,0,4,NA,NA,NA,NA,-21,43,1021,-11,NW,1.79,0,0
2,2010,1,1,1,4,NA,NA,NA,NA,-21,47,1020,-12,NW,4.92,0,0
3,2010,1,1,2,4,NA,NA,NA,NA,-21,43,1019,-11,NW,6.71,0,0
4,2010,1,1,3,4,NA,NA,NA,NA,-21,55,1019,-14,NW,9.84,0,0
5,2010,1,1,4,4,NA,NA,NA,NA,-20,51,1018,-12,NW,12.97,0,0
6,2010,1,1,5,4,NA,NA,NA,NA,-19,47,1017,-10,NW,16.1,0,0
7,2010,1,1,6,4,NA,NA,NA,NA,-19,44,1017,-9,NW,19.23,0,0
8,2010,1,1,7,4,NA,NA,NA,NA,-19,44,1017,-9,NW,21.02,0,0
9,2010,1,1,8,4,NA,NA,NA,NA,-19,44,1017,-9,NW,24.15,0,0
10,2010,1,1,9,4,NA,NA,NA,NA,-20,37,1017,-8,NW,27.28,0,0
11,2010,1,1,10,4,NA,NA,NA,NA,-19,37,1017,-7,NW,31.3,0,0
12,2010,1,1,11,4,NA,NA,NA,NA,-18,35,1017,-5,NW,34.43,0,0
13,2010,1,1,12,4,NA,NA,NA,NA,-19,32,1015,-5,NW,37.56,0,0
14,2010,1,1,13,4,NA,NA,NA,NA,-18,30,1015,-3,NW,40.69,0,0
15,2010,1,1,14,4,NA,NA,NA,NA,-18,28,1014,-2,NW,43.82,0,0
16,2010,1,1,15,4,NA,NA,NA,NA,-18,26,1014,-1,cv,0.89,0,0
17,2010,1,1,16,4,NA,NA,NA,NA,-19,25,1015,-2,NW,1.79,0,0
18,2010,1,1,17,4,NA,NA,NA,NA,-18,30,1015,-3,NW,2.68,0,0
19,2010,1,1,18,4,NA,NA,NA,NA,-18,35,1016,-5,NE,1.79,0,0
20,2010,1,1,19,4,NA,NA,NA,NA,-17,35,1017,-4,NW,1.79,0,0
21,2010,1,1,20,4,NA,NA,NA,NA,-17,38,1017,-5,cv,0.89,0,0
22,2010,1,1,21,4,NA,NA,NA,NA,-17,38,1018,-5,NW,1.79,0,0
23,2010,1,1,22,4,NA,NA,NA,NA,-17,38,1018,-5,NW,2.68,0,0
24,2010,1,1,23,4,NA,NA,NA,129,-17,41,1020,-5,cv,0.89,0,0
25,2010,1,2,0,4,NA,NA,NA,148,-16,38,1020,-4,SE,1.79,0,0
26,2010,1,2,1,4,NA,NA,NA,159,-15,42,1020,-4,SE,2.68,0,0
27,2010,1,2,2,4,NA,NA,NA,181,-11,63.5,1021,-5,SE,3.57,0,0
28,2010,1,2,3,4,NA,NA,NA,138,-7,85,1022,-5,SE,5.36,0,0
29,2010,1,2,4,4,NA,NA,NA,109,-7,85,1022,-5,SE,6.25,0,0
30,2010,1,2,5,4,NA,NA,NA,105,-7,92,1022,-6,SE,7.14,0,0
31,2010,1,2,6,4,NA,NA,NA,124,-7,92,1023,-6,SE,8.93,0,0
32,2010,1,2,7,4,NA,NA,NA,120,-7,85,1024,-5,SE,10.72,0,0
33,2010,1,2,8,4,NA,NA,NA,132,-8,85,1024,-6,SE,12.51,0,0
34,2010,1,2,9,4,NA,NA,NA,140,-7,85,1025,-5,SE,14.3,0,0
35,2010,1,2,10,4,NA,NA,NA,152,-7,85,1026,-5,SE,17.43,0,0
36,2010,1,2,11,4,NA,NA,NA,148,-8,79,1026,-5,SE,20.56,0,0
37,2010,1,2,12,4,NA,NA,NA,164,-8,79,1026,-5,SE,23.69,0,0
38,2010,1,2,13,4,NA,NA,NA,158,-8,79,1025,-5,SE,27.71,0,0
39,2010,1,2,14,4,NA,NA,NA,154,-9,73,1025,-5,SE,31.73,0,0
40,2010,1,2,15,4,NA,NA,NA,159,-9,73,1025,-5,SE,35.75,0,0
41,2010,1,2,16,4,NA,NA,NA,164,-9,73,1026,-5,SE,37.54,0,0
42,2010,1,2,17,4,NA,NA,NA,170,-8,79,1027,-5,SE,39.33,0,0
43,2010,1,2,18,4,NA,NA,NA,149,-8,79,1027,-5,SE,42.46,0,0
44,2010,1,2,19,4,NA,NA,NA,154,-8,79,1028,-5,SE,44.25,0,0
45,2010,1,2,20,4,NA,NA,NA,164,-7,85,1028,-5,SE,46.04,0,0
46,2010,1,2,21,4,NA,NA,NA,156,-7,85,1027,-5,SE,49.17,0,0
47,2010,1,2,22,4,NA,NA,NA,126,-8,85,1028,-6,SE,52.3,0,0
48,2010,1,2,23,4,NA,NA,NA,90,-8,85,1027,-6,SE,55.43,0,0
49,2010,1,3,0,4,NA,NA,NA,63,-7,92,1027,-6,SE,58.56,0.4,0.4
50,2010,1,3,1,4,NA,NA,NA,65,-8,85,1026,-6,SE,61.69,0.5,0.9
51,2010,1,3,2,4,NA,NA,NA,55,-8,92,1026,-7,SE,65.71,0.5,1.4
52,2010,1,3,3,4,NA,NA,NA,65,-8,92,1025,-7,SE,68.84,0.7,2.1
53,2010,1,3,4,4,NA,NA,NA,83,-8,92,1024,-7,SE,72.86,1.2,3.3
54,2010,1,3,5,4,NA,NA,NA,91,-9,92,1024,-8,SE,76.88,0.7,4
55,2010,1,3,6,4,NA,NA,NA,86,-10,85,1024,-8,SE,80.9,1,5
56,2010,1,3,7,4,NA,NA,NA,82,-10,92,1024,-9,SE,84.92,0.7,5.7
57,2010,1,3,8,4,NA,NA,NA,86,-10,92,1024,-9,SE,89.84,0.5,6.2
58,2010,1,3,9,4,NA,NA,NA,78,-11,85,1023,-9,SE,93.86,0.7,6.9
59,2010,1,3,10,4,NA,NA,NA,98,-11,85,1023,-9,SE,97.88,0.4,7.3
60,2010,1,3,11,4,NA,NA,NA,107,-11,85,1022,-9,SE,102.8,0.5,7.8
61,2010,1,3,12,4,NA,NA,NA,90,-11,85,1021,-9,SE,105.93,1.1,8.9
62,2010,1,3,13,4,NA,NA,NA,96,-11,85,1020,-9,SE,111.74,0.5,9.4
63,2010,1,3,14,4,NA,NA,NA,95,-11,85,1020,-9,SE,116.66,0.3,9.7
64,2010,1,3,15,4,NA,NA,NA,86,-11,85,1020,-9,SE,121.58,0.5,10.2
65,2010,1,3,16,4,NA,NA,NA,70,-11,85,1020,-9,SE,124.71,0.2,10.4
66,2010,1,3,17,4,NA,NA,NA,61,-11,85,1020,-9,SE,127.84,0.1,10.5
67,2010,1,3,18,4,NA,NA,NA,53,-11,85,1021,-9,cv,0.89,0.4,10.9
68,2010,1,3,19,4,NA,NA,NA,71,-11,85,1022,-9,cv,1.78,0.3,11.2
69,2010,1,3,20,4,NA,NA,NA,72,-10,92,1022,-9,NW,4.02,0,0
70,2010,1,3,21,4,NA,NA,NA,76,-11,92,1023,-10,NW,7.15,0,0
71,2010,1,3,22,4,NA,NA,NA,73,-11,85,1023,-9,NW,11.17,0,0
72,2010,1,3,23,4,NA,NA,NA,79,-12,92,1023,-11,NW,14.3,0,0
73,2010,1,4,0,4,NA,NA,NA,58,-14,85,1023,-12,NW,16.09,0,0
74,2010,1,4,1,4,NA,NA,NA,25,-16,56,1023,-9,NW,21.9,0,0
75,2010,1,4,2,4,NA,NA,NA,26,-17,56,1024,-10,NW,29.95,0,0
76,2010,1,4,3,4,NA,NA,NA,28,-18,56,1024,-11,NW,39.78,0,0
77,2010,1,4,4,4,NA,NA,NA,26,-19,51,1025,-11,NW,48.72,0,0
78,2010,1,4,5,4,NA,NA,NA,20,-20,51,1026,-12,NW,55.87,0,0
79,2010,1,4,6,4,NA,NA,NA,29,-21,47,1027,-12,NW,64.81,0,0
80,2010,1,4,7,4,NA,NA,NA,26,-21,51,1027,-13,NW,73.75,0,0
81,2010,1,4,8,4,NA,NA,NA,27,-22,46,1028,-13,NW,80.9,0,0
82,2010,1,4,9,4,NA,NA,NA,27,-22,46,1029,-13,NW,90.73,0,0
83,2010,1,4,10,4,NA,NA,NA,25,-22,43,1030,-12,NW,100.56,0,0
84,2010,1,4,11,4,NA,NA,NA,29,-23,39,1031,-12,NW,108.61,0,0
85,2010,1,4,12,4,NA,NA,NA,32,-21,43,1030,-11,NW,117.55,0,0
86,2010,1,4,13,4,NA,NA,NA,28,-20,43,1030,-10,NW,127.38,0,0
87,2010,1,4,14,4,NA,NA,NA,29,-21,40,1030,-10,NW,136.32,0,0
88,2010,1,4,15,4,NA,NA,NA,30,-21,37,1030,-9,NW,145.26,0,0
89,2010,1,4,16,4,NA,NA,NA,30,-21,37,1031,-9,NW,152.41,0,0
90,2010,1,4,17,4,NA,NA,NA,28,-20,47,1032,-11,NW,159.56,0,0
91,2010,1,4,18,4,NA,NA,NA,26,-23,36,1032,-11,NW,165.37,0,0
92,2010,1,4,19,4,NA,NA,NA,31,-21,47,1033,-12,NW,171.18,0,0
93,2010,1,4,20,4,NA,NA,NA,33,-24,36,1034,-12,NW,180.12,0,0
94,2010,1,4,21,4,NA,NA,NA,29,-24,39,1034,-13,NW,187.27,0,0
95,2010,1,4,22,4,NA,NA,NA,31,-24,39,1035,-13,NW,195.32,0,0
96,2010,1,4,23,4,NA,NA,NA,30,-26,38,1035,-15,NW,198.45,0,0
97,2010,1,5,0,4,NA,NA,NA,34,-26,45,1035,-17,NW,201.58,0,0
98,2010,1,5,1,4,NA,NA,NA,27,-26,49,1035,-18,NW,205.6,0,0
99,2010,1,5,2,4,NA,NA,NA,25,-26,53,1035,-19,NW,208.73,0,0

代码展示:

package com.spark

import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.{DataFrame, SparkSession}

object DataFrameTest {

  //需求: 查看每个月的统计数量

  def main(args: Array[String]): Unit = {
    Logger.getLogger("org").setLevel(Level.ERROR)

    //1,创建SparkSession
    val spark = SparkSession.builder()
      .master("local[6]")
      .appName(this.getClass.getSimpleName)
      .getOrCreate()

    //隐式转换
    import spark.implicits._
    //2,读取数据集
    val sourceDF: DataFrame = spark.read
      .option("header",true) //忽略消息头
      .csv("E:\\Project\\Spark\\spark-sql\\input\\BeijingPM20100101_20151231.csv")

    //sourceDF.show()
    //查看DataFrame 的 schema 信息,要意识到 DataFrame 中是有结构信息的,叫做Schema
    sourceDF.printSchema()

    //3,处理
    /**
     * 1,选择列
     * 2,过滤掉NA的PM记录
     * 3,分组 select year,month, count (pm_Dongsi) from ... where pm_Dongsi pm_Dongsi != NA group by year , month
     * 4,聚合
     */

    println("--------------表结构和表结果的分界线---------------")
    // 得出结论
    sourceDF.select('year,'month,'PM_Dongsi)
      .where('PM_Dongsi =!= "NA")
      .groupBy('year,'month)
      .count()
      .show()


    println("---------代码查询和SQL语句查询的分界线--------------")

    //创建临时表
    sourceDF.createOrReplaceTempView("pm")
    //year,month,day,hour,season,PM_Dongsi
    val resultDF = spark.sql("select year,month,count(PM_Dongsi) from pm where PM_Dongsi != 'NA' group by year,month")

    resultDF.show()

    spark.stop()


  }
}

总结

1,DataFrame 是一个类似于关系型数据库表的函数式组件
2,DataFrame 一般处理结构化数据和半结构化数据
3,DataFrame 具有数据对象的 Schema 信息
4,可以使用命令式的 API 操作 DataFrame, 同时也可以使用 SQL 操作 DataFrame
5,DataFrame 可以由一个已经存在的集合直接创建, 也可以读取外部的数据源来创建

Dataset 和 DataFrame 的异同

目标

理解 Dataset 和 DataFrame 之间的关系

DataFrame 就是 Dataset

根据前面的内容, 可以得到如下信息

  • Dataset 中可以使用列来访问数据, DataFrame 也可以
  • Dataset 的执行是优化的, DataFrame 也是
  • Dataset 具有命令式 API, 同时也可以使用 SQL 来访问, DataFrame 也可以使用这两种不同的方式访问

所以这件事就比较蹊跷了, 两个这么相近的东西为什么会同时出现在 SparkSQL 中呢?

在这里插入图片描述
确实, 这两个组件是同一个东西, DataFrame 是 Dataset 的一种特殊情况, 也就是说 DataFrame 是 Dataset[Row] 的别名

DataFrame 和 Dataset 所表达的语义不同

第一点: DataFrame 表达的含义是一个支持函数式操作的 表, 而 Dataset 表达是是一个类似 RDD 的东西, Dataset 可以处理任何对象

第二点: DataFrame 中所存放的是 Row 对象, 而 Dataset 中可以存放任何类型的对象

val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._
//1
val df: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()       
//2
val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS() 

1,DataFrame 就是 Dataset[Row]
2,Dataset 的范型可以是任意类型

第三点: DataFrame 的操作方式和 Dataset 是一样的, 但是对于强类型操作而言, 它们处理的类型不同

@Test
  def dataFrame3(): Unit ={
    Logger.getLogger("org").setLevel(Level.ERROR)
    //创建SparkSession
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[6]")
      .getOrCreate()


    //隐式转换
    import spark.implicits._
    val PersonList: Seq[Person] = Seq(Person("zhangsan", 15), Person("lisi", 20))

    //TODO DataFrame 是弱类型的
    val df: DataFrame = PersonList.toDF()
    //根据下标取 因为年龄是int 类型,所以转换为getAS[Int]
    df.map( (row:Row) => Row(row.get(0),row.getAs[Int](1) * 2) ) (RowEncoder.apply(df.schema))
      .show()

    //弱类型编译不安全
    df.groupBy("name.school")


    //TODO 是强类型的
    val ds: Dataset[Person] = PersonList.toDS()
    //根据类的类型取
    ds.map((person:Person)  => Person(person.name,person.age * 2)).show()


    //DataSet 代表的操作是,类型安全的,编译时安全
    //ds.filter( person => person.school)

  }
case class Person(name:String,age:Int)

第四点: DataFrame 只能做到运行时类型检查, Dataset 能做到编译和运行时都有类型检查

  1. DataFrame 中存放的数据以 Row 表示, 一个 Row 代表一行数据, 这和关系型数据库类似
  2. DataFrame 在进行 map 等操作的时候, DataFrame 不能直接使用 Person 这样的 Scala 对象, 所以无法做到编译时检查
  3. Dataset 表示的具体的某一类对象, 例如 Person, 所以再进行 map 等操作的时候, 传入的是具体的某个 Scala 对象, 如果调用错了方法, 编译时就会被检查出来
val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
ds.map(person => person.hello) 

这行代码明显报错, 无法通过编译

Row 是什么?

Row 对象表示的是一个 行
Row 的操作类似于 Scala 中的 Map 数据类型

 @Test
  def row(): Unit ={
    //1,Row 如何创建 它是什么
    //row 配合 schema对象才会有列名
    val p = Person("zhangsan", 15)
    val row = Row("zhangsan",16)

    println(row.schema) //没有类型


    //2,如何从Row 中获取数据
    row.getString(0)
    row.getInt(1)


    //3,Row也是样例类
    row match {
      case Row(name,age) =>println(name,age)
    }
  }
case class Person(name:String,age:Int)

DataFrame 和 Dataset 之间可以非常简单的相互转换

case class Person(name:String,age:Int)
val spark: SparkSession = new sql.SparkSession.Builder()
  .appName("hello")
  .master("local[6]")
  .getOrCreate()

import spark.implicits._

val df: DataFrame = Seq(People("zhangsan", 15), People("lisi", 15)).toDF()
val ds_fdf: Dataset[People] = df.as[People]

val ds: Dataset[People] = Seq(People("zhangsan", 15), People("lisi", 15)).toDS()
val df_fds: DataFrame = ds.toDF()

总结

1,DataFrame 就是 Dataset, 他们的方式是一样的, 也都支持 API 和 SQL 两种操作方式
2,DataFrame 只能通过表达式的形式, 或者列的形式来访问数据, 只有 Dataset 支持针对于整个对象的操作
3,DataFrame 中的数据表示为 Row, 是一个行的概念

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值