目录
一 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 对象
在 Spark SQL 中 SparkSession 是创建DataFrame 和执行 SQL 的入口,创建 DataFrame
有三种方式:通过Spark 的数据源进行创建;从一个存在的RDD 进行转换;还可以从Hive
Table 进行查询返回。
1)从 Spark 数据源进行创建
scala> spark.read.
{"username":"zhangsan","age":20}
{"username":"lisi","age":30}
{"username":"wangwu","age":25}
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 进行查询返回
在后续章节中讨论
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|
+---+--------+
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|
+---+-----+
在 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|
+--------+---+
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 :25
scala> rdd.collect
res4: Array[org.apache.spark.sql.Row] = Array([mingyu,18], [chenchen,18])
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
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|
+--------+---+
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 :25
scala> rdd2.collect
res9: Array[User] = Array(User(mingyu,18), User(chenchen,18))
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|
+----+---+
在 SparkSQL 中 Spark 为我们提供了两个新的抽象,分别是 DataFrame 和 DataSet。他们和 RDD 有什么区别呢?首先从版本的产生上来看:
如果同样的数据都给到这三个数据结构,他们分别计算之后,都会给出相同的结果。不同是的他们的执行效率和执行方式。在后期的 Spark 版本中,DataSet 有可能会逐步取代RDD和 DataFrame 成为唯一的API 接口。
1)RDD
2)DataFrame
3)DataSet