SparkSQL核心编程

目录

 一 DataFrame

1.1 创建 DataFrame

1.2 SQL语法

1.3 DSL语法

1.4 RDD转为DataFrame

1.5 DataFrame转为RDD

二 DataSet

2.1 创建DataSet

2.2 RDD转为DataSet

2.3 DataSet转为RDD

2.4 DataFrame 和 DataSet 转换

2.5 RDD、DataFrame、DataSet 三者的关系

2.5.1 三者的共性

2.5.2 三者的区别

2.5.3 三者的互相转换


SparkSQL 概述_一抹鱼肚白的博客-CSDN博客

        SparkSession 是 Spark 最新的 SQL 查询起始点,实质上是 SQLContext 和HiveContext 的组合,所以在 SQLContex 和HiveContext 上可用的API 在 SparkSession 上同样是可以使用的。SparkSession 内部封装了 SparkContext,所以计算实际上是由 sparkContext 完成的。当我们使用 spark-shell 的时候, spark 框架会自动的创建一个名称叫做 spark 的SparkSession 对象, 就像我们以前可以自动获取到一个 sc 来表示 SparkContext 对象

 一 DataFrame

1.1 创建 DataFrame

        在 Spark SQL 中 SparkSession 是创建DataFrame 和执行 SQL 的入口,创建 DataFrame

有三种方式:通过Spark 的数据源进行创建;从一个存在的RDD 进行转换;还可以从Hive

Table 进行查询返回。

1)从 Spark 数据源进行创建

  • 查看 Spark 支持创建文件的数据源格式
scala> spark.read.
  • 在 spark 的 bin/data 目录中创建 user.json 文件
{"username":"zhangsan","age":20}
{"username":"lisi","age":30}
{"username":"wangwu","age":25}
  • 读取 json 文件创建DataFrame
scala> val df = spark.read.json("input/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, username: string]                                              

scala> df.show
+---+--------+
|age|username|
+---+--------+
| 20|zhangsan|
| 30|    lisi|
| 25|  wangwu|
+---+--------+

注意:如果从内存中获取数据,spark 可以知道数据类型具体是什么。如果是数字,默认作Int 处理;但是从文件中读取的数字,不能确定是什么类型,所以用 bigint 接收,可以和

Long 类型转换,但是和 Int 不能进行转换

2)从 RDD 进行转换

scala> sc.makeRDD(List(("mingyu",18),("chenchen",18))).map(t=>User(t._1,t._2)).toDF.show
+--------+---+
|    name|age|
+--------+---+
|  mingyu| 18|
|chenchen| 18|
+--------+---+

3)从 Hive Table 进行查询返回

在后续章节中讨论

1.2 SQL语法

        SQL 语法风格是指我们查询数据的时候使用 SQL 语句来查询,这种风格的查询必须要有临时视图或者全局视图来辅助

1)读取JSON 文件创建DataFrame

scala> val df = spark.read.json("input/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, username: string]                                              

scala> df.show
+---+--------+
|age|username|
+---+--------+
| 20|zhangsan|
| 30|    lisi|
| 25|  wangwu|
+---+--------+

2)对DataFrame创建一个临时表

scala> df.createOrReplaceTempView("user")

3)通过SQL语句实现全表查询

scala> val result = spark.sql("select * from user")
result: org.apache.spark.sql.DataFrame = [age: bigint, username: string]

4)结果展示

scala> result.show
+---+--------+
|age|username|
+---+--------+
| 20|zhangsan|
| 30|    lisi|
| 25|  wangwu|
+---+--------+

注意:普通临时表是 Session 范围内的,如果想应用范围内有效,可以使用全局临时表。使用全局临时表时需要全路径访问,如:global_temp.people

scala> spark.newSession().sql("select * from user")
org.apache.spark.sql.AnalysisException: Table or view not found: user; line 1 pos 14;
'Project [*]
+- 'UnresolvedRelation [user]

  at org.apache.spark.sql.catalyst.analysis.package$AnalysisErrorAt.failAnalysis(package.scala:42)
  at org.apache.spark.sql.catalyst.analysis.CheckAnalysis.$anonfun$checkAnalysis$1(CheckAnalysis.scala:106)
  at org.apache.spark.sql.catalyst.analysis.CheckAnalysis.$anonfun$checkAnalysis$1$adapted(CheckAnalysis.scala:92)
  at org.apache.spark.sql.catalyst.trees.TreeNode.foreachUp(TreeNode.scala:177)
  at org.apache.spark.sql.catalyst.trees.TreeNode.$anonfun$foreachUp$1(TreeNode.scala:176)
  at org.apache.spark.sql.catalyst.trees.TreeNode.$anonfun$foreachUp$1$adapted(TreeNode.scala:176)
  at scala.collection.immutable.List.foreach(List.scala:392)
  at org.apache.spark.sql.catalyst.trees.TreeNode.foreachUp(TreeNode.scala:176)
  at org.apache.spark.sql.catalyst.analysis.CheckAnalysis.checkAnalysis(CheckAnalysis.scala:92)
  at org.apache.spark.sql.catalyst.analysis.CheckAnalysis.checkAnalysis$(CheckAnalysis.scala:89)
  at org.apache.spark.sql.catalyst.analysis.Analyzer.checkAnalysis(Analyzer.scala:130)
  at org.apache.spark.sql.catalyst.analysis.Analyzer.$anonfun$executeAndCheck$1(Analyzer.scala:156)
  at org.apache.spark.sql.catalyst.plans.logical.AnalysisHelper$.markInAnalyzer(AnalysisHelper.scala:201)
  at org.apache.spark.sql.catalyst.analysis.Analyzer.executeAndCheck(Analyzer.scala:153)
  at org.apache.spark.sql.execution.QueryExecution.$anonfun$analyzed$1(QueryExecution.scala:68)
  at org.apache.spark.sql.catalyst.QueryPlanningTracker.measurePhase(QueryPlanningTracker.scala:111)
  at org.apache.spark.sql.execution.QueryExecution.$anonfun$executePhase$1(QueryExecution.scala:133)
  at org.apache.spark.sql.SparkSession.withActive(SparkSession.scala:763)
  at org.apache.spark.sql.execution.QueryExecution.executePhase(QueryExecution.scala:133)
  at org.apache.spark.sql.execution.QueryExecution.analyzed$lzycompute(QueryExecution.scala:68)
  at org.apache.spark.sql.execution.QueryExecution.analyzed(QueryExecution.scala:66)
  at org.apache.spark.sql.execution.QueryExecution.assertAnalyzed(QueryExecution.scala:58)
  at org.apache.spark.sql.Dataset$.$anonfun$ofRows$2(Dataset.scala:99)
  at org.apache.spark.sql.SparkSession.withActive(SparkSession.scala:763)
  at org.apache.spark.sql.Dataset$.ofRows(Dataset.scala:97)
  at org.apache.spark.sql.SparkSession.$anonfun$sql$1(SparkSession.scala:606)
  at org.apache.spark.sql.SparkSession.withActive(SparkSession.scala:763)
  at org.apache.spark.sql.SparkSession.sql(SparkSession.scala:601)
  ... 47 elided

5)对于DataFrame 创建一个全局表

scala> df.createGlobalTempView("emp")

6)再次查询

scala> spark.newSession().sql("select * from global_temp.people").show
+---+--------+
|age|username|
+---+--------+
| 20|zhangsan|
| 30|    lisi|
| 25|  wangwu|
+---+--------+

1.3 DSL语法

        DataFrame 提供一个特定领域语言(domain-specific language, DSL)去管理结构化的数据。可以在 Scala, Java, Python 和 R 中使用 DSL,使用 DSL 语法风格不必去创建临时视图了

1)读取JSON 文件创建DataFrame

scala> val df = spark.read.json("input/user.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, username: string]                                              

scala> df.show
+---+--------+
|age|username|
+---+--------+
| 20|zhangsan|
| 30|    lisi|
| 25|  wangwu|
+---+--------+

2)查看DataFrame 的 Schema 信息

scala> df.printSchema
root
 |-- age: long (nullable = true)
 |-- username: string (nullable = true)

3)查看"username"信息

scala> df.select("username").show
+--------+
|username|
+--------+
|zhangsan|
|    lisi|
|  wangwu|
+--------+

4)查看"username"列数据以及"age+1"数据

注意:要用$或者'都用其中一种,不能混用,且查询字段都要使用

scala> df.select('username,'age+1).show
scala> df.select($"username",$"age"+1).show
+--------+---------+
|username|(age + 1)|
+--------+---------+
|zhangsan|       21|
|    lisi|       31|
|  wangwu|       26|
+--------+---------+

5)按照"age"分组,查看数据条数

scala> df.groupBy("age").count.show
+---+-----+
|age|count|
+---+-----+
| 25|    1|
| 30|    1|
| 20|    1|
+---+-----+

1.4 RDD转为DataFrame

        在 IDEA 中开发程序时,如果需要RDD 与DF 或者DS 之间互相操作,那么需要引入

import spark.implicits._

        这里的 spark 不是Scala 中的包名,而是创建的 sparkSession 对象的变量名称,所以必须先创建 SparkSession 对象再导入。这里的 spark 对象不能使用var 声明,因为 Scala 只支持

val 修饰的对象的引入。

        spark-shell 中无需导入,自动完成此操作

scala> val idRDD = sc.textFile("data/id.txt") 

scala> idRDD.toDF("id").show
+---+
| id|
+---+
| 1|
| 2|
| 3|
| 4|
+---+

实际开发中,一般通过样例类将 RDD 转换为 DataFrame

scala> sc.makeRDD(List(("mingyu",18),("chenchen",18))).map(t=>User(t._1,t._2)).toDF.show
+--------+---+
|    name|age|
+--------+---+
|  mingyu| 18|
|chenchen| 18|
+--------+---+

1.5 DataFrame转为RDD

DataFrame 其实就是对RDD 的封装,所以可以直接获取内部的RDD

scala> val df = sc.makeRDD(List(("mingyu",18)("chenchen",18))).map(t=>User(t._1,t._2)).toDF
df: org.apache.spark.sql.DataFrame = [name: string, age: int]

scala> val rdd = df.rdd
rdd: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[26] at rdd at <console>:25

scala> rdd.collect
res4: Array[org.apache.spark.sql.Row] = Array([mingyu,18], [chenchen,18])

二 DataSet

DataSet 是具有强类型的数据集合,需要提供对应的类型信息。

2.1 创建DataSet

1)使用样例类序列创建 DataSet

scala> case class People(name:String,age:Int)
defined class People

scala> val ds = Seq(People("chenchen",22),People("my",23)).toDS
ds: org.apache.spark.sql.Dataset[People] = [name: string, age: int]

scala> ds.show
+--------+---+
|    name|age|
+--------+---+
|chenchen| 22|
|      my| 23|
+--------+---+

2)使用基本类型的序列创建DataSet

scala> val ds = Seq(1,2,3,4,5).toDS
ds: org.apache.spark.sql.Dataset[Int] = [value: int]

scala> ds.show
+-----+
|value|
+-----+
|    1|
|    2|
|    3|
|    4|
|    5|
+-----+

注意:在实际使用的时候,很少用到把序列转换成DataSet,更多的是通过RDD 来得到DataSet

2.2 RDD转为DataSet

        SparkSQL 能够自动将包含有 case 类的RDD 转换成DataSet,case 类定义了 table 的结构,case 类属性通过反射变成了表的列名。Case 类可以包含诸如 Seq 或者 Array 等复杂的结构

scala> case class People(name:String,age:Int)
defined class People

scala> sc.makeRDD(List(("mingyu",18),("chenchen",18))).map(t=>User(t._1,t._2)).toDS.show
+--------+---+
|    name|age|
+--------+---+
|  mingyu| 18|
|chenchen| 18|
+--------+---+

2.3 DataSet转为RDD

DataSet 其实也是对 RDD 的封装,所以可以直接获取内部的RDD

scala> val ds2 = sc.makeRDD(List(("mingyu",18),("chenchen",18))).map(t=>User(t._1,t._2)).toDS
ds2: org.apache.spark.sql.Dataset[User] = [name: string, age: int]

scala> val rdd2 = ds2.rdd
rdd2: org.apache.spark.rdd.RDD[User] = MapPartitionsRDD[41] at rdd at <console>:25

scala> rdd2.collect
res9: Array[User] = Array(User(mingyu,18), User(chenchen,18))

2.4 ​​​​​​​DataFrame DataSet 转换

DataFrame 其实是DataSet 的特例,所以它们之间是可以互相转换的

1) DataFrame  ->  DataSet

scala> val df = sc.makeRDD(List(("辰辰",23),("明宇",24))).toDF("name","age")
df: org.apache.spark.sql.DataFrame = [name: string, age: int]

scala> val ds = df.as[People]
ds: org.apache.spark.sql.Dataset[People] = [name: string, age: int]

scala> ds.show
+----+---+
|name|age|
+----+---+
|辰辰| 23|
|明宇| 24|
+----+---+

2)​​​​​​​ DataSet -> DataFrame

scala> val df2 = ds.toDF
df2: org.apache.spark.sql.DataFrame = [name: string, age: int]

scala> df2.show
+----+---+
|name|age|
+----+---+
|辰辰| 23|
|明宇| 24|
+----+---+

2.5 ​​​​​​​RDDDataFrameDataSet 三者的关系

        在 SparkSQL 中 Spark 为我们提供了两个新的抽象,分别是 DataFrame 和 DataSet。他们和 RDD 有什么区别呢?首先从版本的产生上来看:

  • Spark1.0 => RDD
  • Spark1.3 => DataFrame
  • Spark1.6 => Dataset

        如果同样的数据都给到这三个数据结构,他们分别计算之后,都会给出相同的结果。不同是的他们的执行效率和执行方式。在后期的 Spark 版本中,DataSet 有可能会逐步取代RDD和 DataFrame 成为唯一的API 接口。

2.5.1 三者的共性

  • RDD、DataFrame、DataSet 全都是 spark 平台下的分布式弹性数据集,为处理超大型数据提供便利;
  • 三者都有惰性机制,在进行创建、转换,如 map 方法时,不会立即执行,只有在遇到Action 如 foreach 时,三者才会开始遍历运算;
  • 三者有许多共同的函数,如 filter,排序等;
  • 在对DataFrame 和Dataset 进行操作许多操作都需要这个包:import spark.implicits._(在创建好 SparkSession 对象后尽量直接导入)
  • 三者都会根据 Spark 的内存情况自动缓存运算,这样即使数据量很大,也不用担心会内存溢出
  • 三者都有 partition 的概念
  • DataFrame 和DataSet 均可使用模式匹配获取各个字段的值和类型

2.5.2 三者的区别

1)RDD

  • RDD 一般和 spark mllib 同时使用
  • RDD 不支持 sparksql 操作

2)DataFrame

  • 与 RDD 和 Dataset 不同,DataFrame 每一行的类型固定为Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值
  • DataFrame 与DataSet 一般不与 spark mllib 同时使用
  • DataFrame 与DataSet 均支持 SparkSQL 的操作,比如 select,groupby 之类,还能注册临时表/视窗,进行 sql 语句操作
  • DataFrame 与DataSet 支持一些特别方便的保存方式,比如保存成 csv,可以带上表头,这样每一列的字段名一目了然(后面专门讲解)

3)DataSet

  • Dataset 和DataFrame 拥有完全相同的成员函数,区别只是每一行的数据类型不同 DataFrame 其实就是DataSet 的一个特例 type DataFrame = Dataset[Row]
  • DataFrame 也可以叫Dataset[Row],每一行的类型是 Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知,只能用上面提到的 getAS 方法或者共性中的第七条提到的模式匹配拿出特定字段。而Dataset 中,每一行是什么类型是不一定的,在自定义了 case class 之后可以很自由的获得每一行的信息

2.5.3 三者的互相转换

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值