Spark-SQL

创建DataFrame

创建DataFrame首先需要SparkSession对象,创建方式:

val spark = SparkSession.builder()
  .master("local[*]")
  .appName("SparkSQL")
  .getOrCreate()

创建完成后,通过SparkSession对象调用createDataFrame()方法创建DataFrame

DataFrame可以使用以下方式创建:

  1. 使用RDD/Seq/List转换,可以在转化时指定列名,注意,要导入SparkSession对象的隐式转换,否则无法调用toDF函数
val conf = new SparkConf().setMaster("local[*]").setAppName("createDF")
val spark = SparkSession.builder().config(conf).getOrCreate()
val sc = spark.sparkContext
import spark.implicits._
val rdd = sc.makeRDD(Seq((1, "a"), (2, "b"), (3, "c")))
val dataFrame = rdd.toDF("id", "value")
dataFrame.printSchema()
dataFrame.show()
/*
root
 |-- id: integer (nullable = false)
 |-- value: string (nullable = true)

+---+-----+
| id|value|
+---+-----+
|  1|    a|
|  2|    b|
|  3|    c|
+---+-----+

如果不指定列明,则会按照列的排序,以_1,_2,。。。。来命名列

如果指定列名时列明的数量和实际列的数量不一致,则会抛出异常
*/
  1. 使用SparkSession对象的createDataFrame函数创建,该方法首先传入一个RDD对象作为数据源,再传入一个schema作为表结构
val conf = new SparkConf().setMaster("local[*]").setAppName("createDF")
val spark = SparkSession.builder().config(conf).getOrCreate()
val sc = spark.sparkContext
import spark.implicits._
val rdd = sc.makeRDD(Seq((1, "zs"), (2, "ls"), (3, "ww")))
val schema = StructType(StructField("id", IntegerType) :: StructField("name", StringType) :: Nil)
// 需要注意,createDataFrame只接受RDD[org.apache.spark.sql.Row]类型的RDD
val dataFrame = spark.createDataFrame(rdd.map(e => Row(e._1, e._2)), schema)
dataFrame.printSchema()
dataFrame.show()
  1. 从文件中读取,通过SparkSession对象调用read函数,可以获得一个DataFrameReaderDataFrameReader类中定义了从各种类型的文件中读取数据的方法,例如:jdbc、json、csv、textFile等,还可以调用format函数自行指定,下面演示一个从csv文件中读取数据案例:
    要读取的目标文件格式:
    在这里插入图片描述
val conf = new SparkConf().setMaster("local[*]").setAppName("createDF")
val spark = SparkSession.builder().config(conf).getOrCreate()
val sc = spark.sparkContext
val dataFrame = spark.read.format("csv").option("header","true").load("in/event_attendees.csv")
dataFrame.printSchema()
dataFrame.show(5)
/*
root
 |-- event: string (nullable = true)
 |-- yes: string (nullable = true)
 |-- maybe: string (nullable = true)
 |-- invited: string (nullable = true)
 |-- no: string (nullable = true)

+----------+--------------------+--------------------+--------------------+--------------------+
|     event|                 yes|               maybe|             invited|                  no|
+----------+--------------------+--------------------+--------------------+--------------------+
|1159822043|1975964455 252302...|2733420590 517546...|1723091036 379587...|3575574655 107729...|
| 686467261|2394228942 268611...|1498184352 645689...|1788073374 733302...|                null|
|1186208412|                null|3320380166 381079...|1379121209 440668682|1728988561 295072...|
|2621578336|                null|                null|                null|                null|
| 855842686|2406118796 355089...|2671721559 176144...|1518670705 880919...|          3500235232|
+----------+--------------------+--------------------+--------------------+--------------------+
only showing top 5 rows
*/

RDD、DataFrame和DataSet三者是可以互相转换的,三者间的区别和联系这里就不详细介绍了,熟练运用三者的函数并灵活转换可以明显提高开发效率
在这里插入图片描述

使用DataFrame

创建视图

使用上面创建好的DataFrame对象,有四个函数可以创建视图:

createGlobalTempView						# 创建全局视图
createTempView								# 创建临时视图
createOrReplaceGlobalTempView				# 创建全局视图,如果视图已存在则替换
createOrReplaceTempView						# 创建临时视图,如果视图已存在则替换

推荐创建临时视图(临时视图指创建的视图仅存在于创建该视图的session中,当session关闭时,临时视图也会随之被清理)

视图创建好后,就可以使用SparkSession对象调用sql()函数,执行SQL语句了:

val conf = new SparkConf().setMaster("local[*]").setAppName("createDF")
val spark = SparkSession.builder().config(conf).getOrCreate()
val sc = spark.sparkContext
val dataFrame:DataFrame = spark.read.format("csv").option("header","true").load("in/event_attendees.csv")
dataFrame.createTempView("user_attend")
spark.sql(
  """
    |select * from user_attend limit 5
  """.stripMargin).show()
/*
+----------+--------------------+--------------------+--------------------+--------------------+
|     event|                 yes|               maybe|             invited|                  no|
+----------+--------------------+--------------------+--------------------+--------------------+
|1159822043|1975964455 252302...|2733420590 517546...|1723091036 379587...|3575574655 107729...|
| 686467261|2394228942 268611...|1498184352 645689...|1788073374 733302...|                null|
|1186208412|                null|3320380166 381079...|1379121209 440668682|1728988561 295072...|
|2621578336|                null|                null|                null|                null|
| 855842686|2406118796 355089...|2671721559 176144...|1518670705 880919...|          3500235232|
+----------+--------------------+--------------------+--------------------+--------------------+
*/

处理数据的三种风格

DataFrame对象可以调用RDD的函数处理数据,也可以创建表视图后,使用SQL语句处理数据。同时DataFrame还可以单独调用类SQL的函数处理数据。

以求取各科成绩top3为例,演示这三种编程风格:

源数据(简化案例,数据以经过处理):

sid,cid,score
01,01,80
01,02,90
01,03,99
02,01,70
02,02,60
02,03,80
03,01,80
03,02,80
03,03,80
04,01,50
04,02,30
04,03,20
05,01,76
05,02,87
06,01,31
06,03,34
07,02,89
07,03,98

首先加载数据

val conf = new SparkConf().setMaster("local[*]").setAppName("createDF")
val spark = SparkSession.builder().config(conf).getOrCreate()
val source = spark.read.format("csv").option("header","true").load("in/score.csv")
source.printSchema()
source.show(5)
/*
root
 |-- sid: string (nullable = true)
 |-- cid: string (nullable = true)
 |-- score: string (nullable = true)

+---+---+-----+
|sid|cid|score|
+---+---+-----+
| 01| 01|   80|
| 01| 02|   90|
| 01| 03|   99|
| 02| 01|   70|
| 02| 02|   60|
+---+---+-----+
only showing top 5 rows
*/

要求以cid为分组条件,求出每个分组内score最高的三条记录

RDD

// 转为RDD(也可以直接用SparkContext去读取)
source.rdd.map(e => (e.getAs[String]("sid"), e.getAs[String]("cid"), e.getAs[String]("score")))
	 // 数据转换为(cid,(sid,score))形式
	 .map(e => (e._2.toInt, (e._1.toInt, e._3.toInt)))
	 // 根据cid分组
	 .groupByKey()
	 // 对分组后的value进行转换,只保留前三名
	 .mapValues(iter => iter.toArray.sortBy(_._2).takeRight(3))
	 // 重新分为多行
	 .flatMap(e => e._2.map(m => (e._1, m._1, m._2)))
	 .collect().foreach(println)
/*
(1,5,76)
(1,1,80)
(1,3,80)
(3,3,80)
(3,7,98)
(3,1,99)
(2,5,87)
(2,7,89)
(2,1,90)
*/

DataFrame

source.select('cid, 'sid, 'score,
  row_number().over(Window.partitionBy('cid).orderBy('score.desc)).as("rank"))
  .where('rank <= lit(3))
  .show()
/*
+---+---+-----+----+
|cid|sid|score|rank|
+---+---+-----+----+
| 01| 01|   80|   1|
| 01| 03|   80|   2|
| 01| 05|   76|   3|
| 03| 01|   99|   1|
| 03| 07|   98|   2|
| 03| 02|   80|   3|
| 02| 01|   90|   1|
| 02| 07|   89|   2|
| 02| 05|   87|   3|
+---+---+-----+----+
*/

SQL语句

    source.createOrReplaceTempView("score")
    spark.sql(
      """
        |select
        |   sid,
        |   cid,
        |   score,
        |   rank
        |from
        |   (select
        |       sid,
        |       cid,
        |       score,
        |       row_number() over(partition by cid order by score desc) as rank
        |   from
        |       score) as tab
        |where
        |   rank <= 3
      """.stripMargin)
      .show()
/*
+---+---+-----+----+
|sid|cid|score|rank|
+---+---+-----+----+
| 01| 01|   80|   1|
| 03| 01|   80|   2|
| 05| 01|   76|   3|
| 01| 03|   99|   1|
| 07| 03|   98|   2|
| 02| 03|   80|   3|
| 01| 02|   90|   1|
| 07| 02|   89|   2|
| 05| 02|   87|   3|
+---+---+-----+----+
*/

保存数据

DataFrame/DataSet对象可以调用write函数获得一个DataFrameWriter对象,类似于DataFrameReader,该对象同样可以连接很多下游写入对象,函数调用方法也与DataFrameReader基本类似。下面以写入hive为例做简单演示:

object CreateDF {
  def main(args: Array[String]): Unit = {
    System.setProperty("HADOOP_USER_NAME", "root")
    val conf = new SparkConf().setMaster("local[*]").setAppName("createDF")
    val spark = SparkSession.builder()
      .config(conf)
      .enableHiveSupport()	// 设置支持Hive
      .config("hive.metastore.uris", "thrift://192.168.226.10:9083")	// 设置HiveIP和端口
      .getOrCreate()
    val dataFrame = spark.read.format("csv").option("header", "true").load("file:///D:\\file\\project\\spark\\after_class\\in\\event_attendees.csv")
    dataFrame.write.mode(SaveMode.Append).saveAsTable("today.event_attendees")
    spark.close()
  }
}

运行后,可以看到程序读取了event_attendees.csv文件中的内容,并将其写入了hive表
在这里插入图片描述
注意事项:

  1. 如果Hive部署在高可用HDFS上,可能会因为无法识别集群名称导致异常,可以像配置HiveIP和端口这样,将HDFS集群名和对应的各个结点通信IP和端口配置上,或者采用更简便的方法,将Hadoop配置文件中的core-site.xmlhdfs-site.xml放在resources文件夹下。IDEA运行时会采用其中的配置
  2. 如果报出权限不足异常,且用户名为本机用户名,可以配置上以上代码第一行的系统设置,切换用户
  3. 存入的目标服务要确认开启,对本例的存入Hive而言,需要开启metastore服务:nohup hive --service metastore &

到这里,就基本完成了使用Spark读取数据、处理数据和写入结果的一套业务操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值