1、SparkSession的构建:
SparkSession:是Spark2.0引入的新概念,为用户提供统一的入口,可以让用户使用Spark的各项功能。
//构建Spark-sql的环境,使用SparkSession统一入口。SparkSession同时也兼容Spark Core、SQL、Streaming、MLLib,
// 都可以对这些组成进行环境初始化
//在之后的构建环境可以使用SparkSession
//构建SparkSession,此处的命名方式也是固定的,通常使用spark
val spark: SparkSession = SparkSession
.builder()
.appName("Demo01SaprkSessio")
.master("local")
//可以通过config做一些配置
//可以设定并行度
// .config("spark.default.parallelism","3")
.getOrCreate()
2、Spark SQL:
指的是一个低延迟,交互式的分布式查询引擎。是基于DataFrame的,可使用标准的SQL和HQL来查询数据
优点:1、速度比MapReduce快大约10倍左右
2、自带优化器,能够基于SQL生成代码并自动优化
3、同样也是支持分布式查询,
4、支持多种语言:scala、python、java
5、支持各种常见的数据源,比如scv、josn、orc、jdbc...
特点:
1、集成:SQL与Spark无缝连接,支持多种语言,可以将RDD转化成DF,使复杂的SQL查询变得简单化
2、统一的数据访问:提供了统一的接口DF,可以加载和查询不同的数据源的数据
3、兼容Hive: 可以实现和hive的连接
4、标准连接:具有行业标准的JDBC和ODBC服务器的连接模式
Spark SQL导入依赖:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_2.12</artifactId>
<version>3.1.3</version>
</dependency>
</dependencies>
3、DataFrame(DF):
指的是一种以RDD为基础的分布式数据集。类似于传统数据库中的一张表。与RDD最主要的区别就是DF带有Schema元信息。总的来说DF就是一种二维表数据集,每一列数据都带有列名和数据类型。可以做到对每一列数据进行操作。
4、Spark SQL的架构:
1、首先是通过DataSource API,是Spark SQL能够支持多种数据源以及数据格式
2、当不同的数据被加载进来的时候,Spark SQL就是将数据映射成DataFrame,进而被DF API进行处理
3、基于DF,Spark SQL提供了两种数据处理方式,分别是:
DSL:是基于DF的一种特殊的语法
SQL/HQL:标准的SQL或者是Hive SQL
5、常见的Source:
a、在Spark SQL中也是需要”行为算子“来触发任务的执行
b、在SparkSQL中行为算子主要分成两种:1、打印 2、保存
1、打印是不同于RDD需要使用foreach加上println进行打印,在SparkSQL中可以直接使用show方法来对数据进行展示,show方法可以接受三个参数:
numRows:指定打印的数据条数
truncate:指定是否将数据截断再进行展示
vertical:执行是否是水平还是垂直展示数据,默认false表示水平展示
补充:
Spark SQL 在打印的时候也可以使用foreach进行打印。
import org.apache.spark.internal.Logging
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}
import org.slf4j.Logger
object Demo02SourceAPICsv extends Logging{
val logger: Logger = log
def main(args: Array[String]): Unit = {
//构建SparkSession
val spark: SparkSession = SparkSession
.builder()
.master("local")
//.appName("Demo02SourceAPI")
.appName(this.getClass.getSimpleName.replace("$",""))
.getOrCreate()
val csvDF: DataFrame = spark
.read
.format("csv")
.option("seq", ",")
.schema("id string,name string,age int,gender string,clazz string")
.load("spark/data/stu/students.txt")
csvDF.foreach(row=>{
// Row对象表示带结构的一行数据,可以通过列名获取对应的列值
val id: String = row.getAs[String]("id")
val name: String = row.getAs[String]("name")
val age: Int = row.getAs[Int]("age")
val gender: String = row.getAs[String]("gender")
val clazz: String = row.getAs[String]("clazz")
println(s"$id,$name, $age,$gender,$clazz")
})
2、保存:通过调用DF调用write进行保存数据,Spark SQL 在保存数据的时候提供了多种写入方式,可以通过mode使用SaveMode进行指定,SaveMode总共提供了四种不同的写入方式:
Append:追加
Ovrewrite:覆盖
ErrorIfExists:存在即报错
Ignore:存在即不写入
c、读文件的一般流程:
spark:构建的SparkSession环境
read:表示读取文件,同理写文件即write
format:指定要读取文件的格式或方式 常见的有:csv、json、parquet、orc、jdbc option:设置读取时的参数,KV格式 读csv时,可通过sep设置列分隔符 读jdbc是,需要指定用jdbcURL、户名、密码、数据库、表名等信息
schema:若数据本身不带结构,则需要通过schema手动指定列名及列的类型 load:加载数据,读文件时需要执行路径
1、读写csv格式文件:逗号分割文件,常用于读取我文本文件
import org.apache.spark.internal.Logging
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}
import org.slf4j.Logger
object Demo02SourceAPICsv extends Logging{
val logger: Logger = log
def main(args: Array[String]): Unit = {
//构建SparkSession
val spark: SparkSession = SparkSession
.builder()
.master("local")
//.appName("Demo02SourceAPI")
.appName(this.getClass.getSimpleName.replace("$",""))
.getOrCreate()
//1、csv格式:逗号分割文件,常用于读取文本文件
/**
* spark:构建的SparkSession环境
* read:表示读取文件,同理写文件即write
* format:指定要读取文件的格式或方式常见的有:csv、json、parquet、orc、jdbc
* option:设置读取时的参数,KV格式,读csv时,可通过sep设置列分隔符,读jdbc是,需要指定用jdbcURL、户名、密码、数据库、表名等信息
* schema:若数据本身不带结构,则需要通过schema手动指定列名及列的类型
* load:加载数据,读文件时需要执行路径
*/
val csvDF: DataFrame = spark
.read
.format("csv")
.option("seq", ",")
.schema("id string,name string,age int,gender string,clazz string")
.load("spark/data/stu/students.txt")
// 同样在SparkSQL中也需要”行为算子“触发任务的执行
// 在SparkSQL中常用的行为算子一般有两种:1、打印 2、保存
/**
* 1、打印:不同于RDD需要使用foreach加上println进行打印
* 在SparkSQL中可以直接使用show方法来对数据进行展示
* show方法可以接收三个参数:
* numRows指定打印的数据条数
* truncate指定是否将数据截断再进行展示
* vertical执行是否水平还是垂直展示数据,默认false表示水平展示
*/
csvDF.show() //默认显示的是二十条数据
csvDF.show(numRows = 15,truncate=false)
csvDF.show(numRows = 15,truncate=1,vertical = false)
/**
* 使用foreach进行打印
*/
// csvDF.foreach(row=>{
// Row对象表示带结构的一行数据,可以通过列名获取对应的列值
// val id: String = row.getAs[String]("id")
// val name: String = row.getAs[String]("name")
// val age: Int = row.getAs[Int]("age")
// val gender: String = row.getAs[String]("gender")
// val clazz: String = row.getAs[String]("clazz")
// println(s"$id,$name, $age,$gender,$clazz")
// })
/**
* 2、保存数据:通过DF调用write进行保存
* Spark SQL在保存的数据的时候提供了多种写入方式,可以通过mode使用SaveMode进行指定
* SaveMode总共提供了四种不同的写入方式:
* Append:追加
* Overwrite:覆盖
* ErrorIfExists:存在即报错
* Ignore:存在即不写入
*/
csvDF
.write
.format("csv")
.option("sep",",")
.mode(SaveMode.Overwrite)
.save("spark/data/stu/csv/")
csvDF
.write
.format("json")
.mode(SaveMode.Overwrite)
.save("spark/data/stu/json/")
csvDF
.write
.format("orc")
.mode(SaveMode.Overwrite)
.save("spark/data/stu/orc/")
csvDF
.write
.format("parquet")
.mode(SaveMode.Overwrite)
.save("spark/data/stu/parquet/")
}
}
2、读写json格式文件:对于json格式文件,是不需要指定结构,但是在存储的时候比较浪费空间
import org.apache.spark.sql.{DataFrame, SparkSession}
object Demo03SourceAPIJson {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession
.builder()
.master("local")
.appName(this.getClass.getSimpleName.replace("$",""))
.getOrCreate()
//对于读取json格式的文件,不需要指定结构,但是在存储时候比较浪费空间空间
val jsonDF: DataFrame = spark
.read
.format("json")
.load("spark/data/stu/json")
jsonDF.show()
}
}
3、读写orc格式文件:同样在读取的时候是不需要指定结构的,由于使用snappy进行压缩,所以空间利用的比较好,同时也是一种列式存储的格式,对于SQL的查询比较友好。
import org.apache.spark.sql.{DataFrame, SparkSession}
object Demo04SourceAPIOrc {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession
.builder()
.master("local")
.appName(this.getClass.getSimpleName.replace("$", ""))
.getOrCreate()
//在使用orc的时候,同样在读取时候不需要指定结构,由于使用了snappy进行压缩,所以空间的利用比较好,
// 该格式同时也是一种列式存储的一种格式,对于SQL的查询比较友好
val orcDF: DataFrame = spark
.read
.format("orc")
.load("spark/data/stu/orc/")
orcDF.show(numRows = 15)
}
}
4、读写parquet格式文件:同样在读取的时候时不需要指定特定的结构,同样使用的是snappy进行压缩,相比较orc,该格式更适合存储嵌套格式的数据
import org.apache.spark.sql.{DataFrame, SparkSession}
object Demo05SourceAPIParquet {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession
.builder()
.appName(this.getClass.getSimpleName)
.master("local")
.getOrCreate()
//parquet:同样在读取的时候不需要指定结构,同样使用的式snappy进行压缩,
//相比较orc,该格式更适合存储嵌套格式的数据
val parquetDF: DataFrame = spark
.read
.load("spark/data/stu/parquet")
parquetDF.show()
}
}
6、Spark SQL读取和写入MysQL
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}
object Demo06SourceAPIMysql {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession
.builder()
.appName(this.getClass.getSimpleName)
.master("local")
//在SparkSql中,如果出现shuffle操作,那么就会默认生成200个切片,即对应200个任务
//可以通过指定参数来改变切片的数量
.config("spark.sql.shuffle.partitions","2")
.getOrCreate()
/**
* 1、对于旧的版本来说,在使用MySQl进行写入数据的时候,数据必须要是元组的形式才能toDF,转换成DF形式的数据。
*
*/
val MysqlDF: DataFrame = spark
.read
.format("jdbc")
.option("driver","com.mysql.jdbc.Driver")
.option("url", "jdbc:mysql://master:3306/student?useUnicode=true&characterEncoding=utf8")
.option("dbtable", "student")
.option("user", "root")
.option("password", "123456")
.load()
MysqlDF.show()
//使用隐式转换,作用是将RDD与DF之间的相互转换。
import spark.implicits._
//需求:统计班级的人数,并将数据写入放到Mysql中
//第一种方式使用RDD的方法:
//将DataFrame转换成RDD
val countRDD: RDD[(String, Int)] = MysqlDF
.rdd
//再从表中获取
.map(row => {(row.getAs[String]("clazz"), 1)})
.reduceByKey((k1, k2) => {k1 + k2})
//toDF:传入的参数表示的是传入的列名,有多少列传入多少参数
val WriteDF: DataFrame = countRDD.toDF("clazz", "count")
//将读取的数据写入到MySQL中
WriteDF
.write
.format("jdbc")
.option("url", "jdbc:mysql://master:3306/student")
.option("dbtable", "clazz_cnt_spark")
.option("user", "root")
.option("password", "123456")
.option("truncate","true")//使用truncate,会让重写的时候是删除数据,不删除表,因此就不会改变表的结构
.mode(SaveMode.Overwrite)//此处的Overwrite会将表删除,然后再按照Spark的映射关系创建表,可能会改变表的结构
.save()
/**
*
* 修改MySQL默认的编码:
* vim /etc/my.cnf
*
* [client]
* default-character-set = utf8mb4
* [mysqld]
* character-set-server = utf8mb4
* collation-server = utf8mb4_general_ci
*
* 重启MySQL服务:systemctl restart mysqld
* */
// //第二种方式:使用spark SQL 的方式,需求:统计每个班级的人数
// //首先需要将DF注册成一张”表“
// MysqlDF.createOrReplaceTempView("MySqlTB")
// spark.sql(
// """
// |select
// | clazz
// | ,count(*) as count_clazz
// |from
// | MySqlTB
// |group by clazz
// |""".stripMargin)
// .show()
//因为后面返回的数据类型依旧是DF的数据类型,所以后续还可以在进行一系类的操作
}
}
/**
* SQL中常见的操作:
* 1、基础操作:
* select 分组字段
* ,聚合操作
* ,使用函数对字段进行处理
* from tb
* where 过滤
* group by 分组
* having 对分组聚合的结果做过滤
* order by 排序,asc升序、desc降序
* limit 限制返回的条数
*
* 2、多表操作:
* 2.1 union联接:表与表之间进行纵向(行的方向)的合并
* union :去重
* union all:不会去重
*
* 2.2 join关联:表与表之间进行横向(列的方向)的合并
* inner内连接:join / inner join
* outer外连接:
* 左外连接:left join / left outer join
* 右外连接:right join / right outer join
* 全外连接:full join / full outer join
*
* 3、高级操作:
* 行列转换:
* 一行转多行:lateral view + UDTF函数
* 多行转一行:group by + collect_list / collect_set
* 自定义函数:UDF、UDTF、UDAF(基本不用)
*
* 4、函数的使用:
* 字符串函数:substring、split、concat、concat_ws......
* 数值函数:round、ceil、floor、abs......
* 条件函数:if、case when、coalesce
* 日期时间函数:from_unixtime、unix_timestamp、datediff、date_add......
* 聚合函数:max、min、sum、count、avg
* 窗口函数:row_number、rank、dense_rank、lag、lead、max、min、sum、count、avg......
*
*/
7、Spark SQL -- DSL的基本操作:
1、在Spark SQL中的DataFrame的一种特定领域语言
2、DSL需要基于DataFrame进行操作,所以需要先构造DataFrame
3、DSL一般支持两种表达方式:
a、字符串表达式:只有在运行的时候才会知道错误出现的地方
b、列表达式$"参数名称":(推荐)当出现错误的时候,会自动的提示,但是在使用列表达式的时候需要先导入隐式转换:
import spark.sql.implicits._
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.{DataFrame, Dataset, Row, SparkSession}
import scala.tools.nsc.{Main, Variance}
object Demo07DSL {
def main(args: Array[String]): Unit = {
//构建sparkSession入口
val spark: SparkSession = SparkSession
.builder()
.appName(this.getClass.getSimpleName)
.master("local")
.config("spark.sql.shuffle.partitions","2")
.getOrCreate()
//读取文件,学生数据表:
val stuDF: DataFrame = spark
.read
.format("csv")
.option("sep", ",")
.schema("id String ,name String,age String,gender String,clazz String")
.load("spark/data/stu/students.txt")
// .show()
/**
* 1、在使用列式转换的时候需要进行隐式转换
* 2、在使用函数的时候需要导入函数包
*/
import spark.implicits._
import org.apache.spark.sql.functions._
/**
* 1、select
*/
//字符串表达式:
stuDF
.select("id","name","gender","clazz")
// .show()
//列表达式:
stuDF
.select($"id",$"name",$"clazz")
// .show()
//需求:使用函数将文科和理科取出来
stuDF
.select($"id", $"name", substring($"clazz", 1, 2))
// .show()
/**
* 需求:对每个班级中的学生按照学号进行降序排序
* 使用开窗函数
* ,row_number() over Window.partitionBy($"clazz").orderBy($"id".desc) as "rn")
*
*/
stuDF
.select($"name",$"age",$"gender",row_number() over Window.partitionBy($"clazz").orderBy($"id".desc) as "rn")
// .show()
/**
* 需求:使用where过滤出来班级是理科三班的同学
* 在Spark中的===是做等值
*/
stuDF
.where($"clazz"==="理科三班")
.select($"id",$"name",$"gender",$"age")
// .show()
/**
* 需求找出班级是理科三班的男同学
* 方式一:使用where,使用 and 接受两个过滤
* 方式二:使用filter,使用RDD的方式进行过滤
*/
stuDF
.where($"clazz" === "理科三班" and $"gender"==="男")
.select($"id", $"name", $"gender", $"age",$"clazz")
// .show()
stuDF
.filter(row=>{
var flag = false
if(row.getAs[String]("clazz")=="理科三班" && row.getAs[String]("gender")=="男")
{
flag = true
}
flag
})
// .show()
/**
* 分组 聚合
* 需求:统计班级的人数
* 1、agg:表示的是聚合的意思
* 2、count:表示的是统计的数量
* 3、countDistinct:表示的是对统计的数量进行去重
*/
stuDF
.groupBy($"clazz")
.agg(count($"id"))
// .show()
//需求:统计班级的人数,并按照人数降序排序,返回前三条数据
stuDF
.groupBy($"clazz")
.agg(countDistinct($"name") as "cnt")
.orderBy($"cnt" desc)
.limit(3)
// .show()
/**
*使用关联
* 对于相同的关联字段的解决方式,因为使用相同的关联字段进行关联的时候会产生ambigious错误:
* 1、对其中某一个DF中的关联字段更改名称,可以使用withColumnRename改变名称
* 2、换一种方式进行关联
*/
//读取学生的分数表
val scoreDF: DataFrame = spark
.read
.format("csv")
.option("seq", ",")
.schema("id String, subject_id String,score Int")
.load("spark/data/stu/score.txt")
// .show()
// inner join 第一种join方式:
scoreDF
.withColumnRenamed("id","sid")
.join(stuDF,$"id"===$"sid")
// .show()
//第二种join方式,通过字符串指定,但是无法指定关联的方式,默认使用的是 inner join:
scoreDF
.join(stuDF,"id")
// .show()
//第三种join的方式,使用list进行关联,可以指定join的方式,当有多个关联的字段,可以在List中添加:
// scoreDF
.join(stuDF,List("id"),"inner")
// .show()
//outer join
scoreDF
.join(stuDF,List("id"),"left")
// .show()
scoreDF
.join(stuDF, List("id"), "right")
// .show()
scoreDF
.join(stuDF, List("id"), "full")
// .show()
//union纵向关联
//对于union来说类似于union all,不会对数据进行去重,可以通过使用distinct来进行对数据的去重
val stuDF01: Dataset[Row] = stuDF.sample(false, 0.01, 1)
val stuDF02: Dataset[Row] = stuDF.sample(false, 0.01, 1)
stuDF01.union(stuDF02)
// .show()
/**
* 行列转换
*/
//一行转多行
val lineDF: DataFrame = spark
.read
.format("csv")
.option("sep", "#")
.schema("line String")
.load("spark/data/words.txt")
lineDF
.select(explode(split($"line",",")) as "clo")
// .show()
val line1DF: DataFrame = spark
.read
.format("csv")
.option("sep", " ")
.schema("id string,name string,weight string")
.load("spark/data/stu/weight.txt")
// line1DF.show()
val newLineDF: DataFrame = line1DF
.select($"id", $"name", explode(split($"weight", ",")) as "weight_count")
// .show()
//多行转一行,可以使用的是GroupBy,结合collect_list或者是collect_set进行聚合压缩
newLineDF
.groupBy($"id",$"name")
//此时输出的结果是一个[150, 180, 170, 200],可以使用一个concat_ws()可以转成string的类型
.agg(concat_ws(",",collect_list($"weight_count")))
// .show()
//快速需求:每个班级的总分前三名
scoreDF
.groupBy($"id")
.agg(sum($"score") as "sum_score")
.join(stuDF,List("id"))
.select($"id",$"name",$"clazz",$"sum_score")
//使用withColumn可以添加一列
.withColumn("rn",row_number() over Window.partitionBy($"clazz").orderBy($"sum_score".desc))
.where($"rn"<=3)
.show()
}
}
8、RDD与DF之间的相互转化
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
object Demo08RDDToDF {
case class Student(id:String,name:String,age:String,gender:String,clazz:String)
def main(args: Array[String]): Unit = {
//构建sparkSession入口
val spark: SparkSession = SparkSession
.builder()
.appName(this.getClass.getSimpleName)
.master("local")
.config("spark.sql.shuffle.partitions", "2")
.getOrCreate()
import spark.implicits._
import org.apache.spark.sql.functions._
/**
* RDD与DF之间的相互转化
*/
val stuDF: DataFrame = spark
.read
.format("csv")
.option("seq", ",")
.schema("id String,name String, age Int,gender String,clazz String")
.load("spark/data/stu/students.txt")
// .show()
/**
* 将DF转化成RDD,并完成需求:统计班级是文科三班并且是女生的同学
*/
//方式一:直接rdd进行转化
stuDF
.rdd
.filter(row=>{
var flag :Boolean=false
if(row.getAs[String]("clazz")=="文科三班" && row.getAs[String]("gender")=="女"){
flag=true
}
flag
}).foreach(println)
//方式二:使用as的方式直接指定数据的类型,再通过模式匹配获取数据
val DFToRDD01: Dataset[(String, String, Int, String, String)] = stuDF
.as[(String, String, Int, String, String)]
val RDD01: RDD[(String, String, Int, String, String)] = DFToRDD01
.rdd
RDD01
.map {
case (id: String, name: String, age: Int, gender: String, clazz: String) =>
s"$id,$name,$age"
}.foreach(println)
//RDD 转化成 DF
//第一种方式使用toDF进行转换,使用之前需要注意的是需要导入函数的包
//第一步:构造一个RDD
val stuRDD: RDD[String] = spark.sparkContext.textFile("spark/data/stu/students.txt")
val stuRDD01: RDD[(String, String, String, String)] = stuRDD.map(kv => {
val id: String = kv.split(",")(0)
val name: String = kv.split(",")(1)
val age: String = kv.split(",")(2)
val gender: String = kv.split(",")(3)
(id, name, age, gender)
})
stuRDD01.toDF("id","name","age","gender")
.show()
//第二种方式:通过样例类对象进行DF的转换,就是创建一个对象,将获取去的数据封装到类中,再通对象调用toDF的方法进行转换
val RDDToDF: DataFrame = stuRDD.map(kv => {
Student(kv.split(",")(0), kv.split(",")(1), kv.split(",")(2), kv.split(",")(3), kv.split(",")(4))
})
//此处不需要指定参数,原因是可以自动的从样例类对象中自动的解析
.toDF()
RDDToDF.show()
}
}
9、SparkSQL 整合hive 使用hive的元数据
在hive的hive-site.xml修改一行配置
在使用hive或Spark-SQL等之前都需要先启动元数据服务已经修改过可以跳过此步骤
<property>
<name>hive.metastore.uris</name>
<value>thrift://master:9083</value>
</property>
将hive-site.xml 复制到spark conf目录下
cp /usr/local/soft/hive-3.1.2/conf/hive-site.xml /usr/local/soft/spark-3.1.3/conf/
启动hive元数据服务
hive --service metastore(推荐 更方便查看日志 随启随用)
或
nohup hive --service metastore >> metastore.log 2>&1 &
将mysql 驱动包复制到spark jars目录下
cp /usr/local/soft/hive-3.1.2/lib/mysql-connector-java-5.1.49.jar /usr/local/soft/spark-3.1.3/jars/
整合好之后在spark-sql 里面就可以使用hive的表了
不能使用yarn-cluster模式
spark-sql --master yarn --deploy-mode client --conf spark.sql.shuffle.partitions=2
在spark-sql中设置运行参数
set spark.sql.shuffle.partitions=2;
10、Spark SQL在本地连接Hive
1、准备工作:
a、导入依赖:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.12</artifactId>
<version>3.1.3</version>
</dependency>
b、将hive-site.xml的配置文件复制到Idea中的Source中
2、运行实例:
object Demo10SparkToHive {
def main(args: Array[String]): Unit = {
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.{DataFrame, SparkSession}
object Demo08SparkOnHive {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession
.builder()
.appName(this.getClass.getSimpleName.replace("$", ""))
.master("local")
.config("spark.sql.shuffle.partitions", "2")
.enableHiveSupport() // 开启Hive的支持
.getOrCreate()
/**
* 通过enableHiveSupport()可以开启Hive的支持
* 需要在pom文件中加入spark-hive的依赖
* 还需要将hive-site.xml放入当前模块的resources目录下
* <dependency>
* <groupId>org.apache.spark</groupId>
* <artifactId>spark-hive_2.12</artifactId>
* <version>3.1.3</version>
* </dependency>
*
*/
spark.sql("use default")
spark.sql(
"""
|select tt1.id
| ,tt1.name
| ,tt1.clazz
| ,tt1.sum_score
| ,tt1.rn
|from (
| select t1.id
| ,t1.name
| ,t1.clazz
| ,t2.sum_score
| ,row_number() over(partition by t1.clazz order by t2.sum_score desc) as rn
| from student t1
| join (
| select student_id as id
| ,sum(sco) as sum_score
| from score
| group by student_id
| ) t2 on t1.id = t2.id
|) tt1 where tt1.rn <= 3
|""".stripMargin).show()
// 将Hive中的表转换成DF
val stuDF: DataFrame = spark.table("student")
val scoDF: DataFrame = spark.table("score")
import spark.implicits._
import org.apache.spark.sql.functions._
scoDF
.groupBy($"student_id")
.agg(sum($"sco") as "sum_score")
.join(stuDF, $"student_id" === $"id", "inner")
.withColumn("rn", row_number() over Window.partitionBy($"clazz").orderBy($"sum_score".desc))
.where($"rn" <= 3)
.show()
}
}
}
}
总结:Spark SQL 写代码的几种方式:
1、在IDEA中将代码编写好打包上传到集群中运行(上线使用)
使用spark-submit提交
2、spark-shell
(repl) 里面使用sqlContext 测试使用,简单任务使用
spark-shell --master yarn --deploy-mode client
只能使用默认的local模式或者yarn-client模式,不能使用yarn-cluster
3、spark-sql
spark-sql --master yarn --deploy-mode client
只能使用默认的local模式或者yarn-client模式,不能使用yarn-cluster