大数据学习day24-------spark07-----1. sortBy是Transformation算子,为什么会触发Action 2. SparkSQL 3. DataFrame的创建 4. DSL风格API语法 5 两种风格(SQL、DSL)计算workcount案例
1. sortBy是Transformation算子,为什么会触发Action
sortBy需要对数据进行全局排序,其需要用到RangePartitioner,而在创建RangePartitioner时需要大概知道有多少数据,以及数据的范围(采样),其内部获取这个范围(rangeBounds)是通过调用sample方法得到,在调用完sample后会调用collect方法,所以会触发Action

2. Spark SQL概述
2.1 Spark SQL定义:
Spark SQL是Spark用来处理结构化数据的一个模块
2.1.1 什么是DataFrames:
与RDD类似,DataFrame也是一个分布式数据容器【抽象的】。然而DataFrame更像DataFrame更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即schema。同时,与Hive类似,DataFrame也是支持嵌套数据类型(struct、array和map)。从API易用性角度上看,DataFrame API 提供的是一套高层的关系操作,比函数式的RDD API要更加友好,门槛更低。由于与R和Pandas的DataFrame类似,Spark DataFrame很好的继承了传统单机数据分析的开发体验

DataFrame = RDD + Schema【更加详细的结构化描述信息】,以后在执行就可以生成执行计划,进行优化。它提供了一个编程抽象叫做DataFrame/Dataset,它可以理解为一个基于RDD数据模型的更高级数据模型,带有结构化元信息(schema),以及sql解析功能

Spark SQL可以将针对DataFrame/Dataset的各类SQL运算,翻译成RDD的各类算子执行计划,从而大大简化数据运算编程(请联想Hive)
3 DateFrame的创建
3.1 sparksql1.x创建DataFrame(SQLContext)
这种形式的写法能更好的理解SQLContext就是对SparkContext的包装增强
package com._51doit.spark07 import com._51doit.spark05.Boy
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{SQLContext,DataFrame} object DataFrameDemo1 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("SparkSQL1x").setMaster("local[*]")
//sc是sparkcore,是用来创建RDD的
val sc = new SparkContext(conf)
// 要创建SQLContext,其相当于是对SparkContext包装增强
//SQLContext就可以创建DataFrame
val sqlContext: SQLContext = new SQLContext(sc)
// 使用SQLContext创建DataFrame(RDD+Schema)
val rdd = sc.parallelize(List("A,18,9999.99", "B,30,999.99", "C,28,999.99"))
//RDD跟schema
val rdd1: RDD[Boy] = rdd.map(line => {
val fields = line.split(",")
val n = fields(0)
val a = fields(1)
val f = fields(2)
Boy(n, a.toInt, f.toDouble)
})
//导入隐式转换
import sqlContext.implicits._
//将RDD转成DataFrame
val df = rdd1.toDF
// 使用SQL风格的API
df.registerTempTable("boy")
// 传入SQL
// sql方法是Transformation
val res: DataFrame = sqlContext.sql("SELECT name, fv, age FROM boy ORDER BY fv DESC, age ASC")
//触发Action,将sql运行的结果收集到Driver端返回
res.show()
//释放资源
sc.stop()
}
}
运行结果

3.2 sparksql2.x创建DataFrame(SparkSession)
SparkSession是对SparkContext的封装,里面有SparkContext的引用,想获得sc直接使用SparkSession调用sparkContext
package com._51doit.spark07 import com._51doit.spark05.Boy
import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession} object SparkSQL2x {
def main(args: Array[String]): Unit = {
// 编程SparkSQL程序,创建DataFrame
val session: SparkSession = SparkSession.builder()
.appName("SparkSQL2x")
.master("local[*]")
.getOrCreate()
// SparkSession 是对SparkContext的封装,里面持有SparkContext的引用
val sc: SparkContext = session.sparkContext
val rdd: RDD[String] = sc.parallelize(List("A,18,9999.99", "B,30,999.99", "C,28,999.99"))
val boyRDD: RDD[Boy] = rdd.map(line => {
val fields: Array[String] = line.split(",")
val n = fields(0)
val a = fields(1)
val f = fields(2)
Boy(n, a.toInt, f.toDouble)
})
// 导入隐式转换
import session.implicits._
// 使用SparkSession创建DataFrame
val df: DataFrame = boyRDD.toDF()
df.createTempView("v_boy")
// 写SQL
val res: DataFrame = session.sql("SELECT name, fv, age FROM v_boy ORDER BY fv DESC, age ASC")
// 触发action
res.show()
session.stop()
}
}
运行结果同上
3.3 使用Scala的case class方式创建DataFrame
Boy
case class Boy(name:String, age:Int, fv: Double)
DataFrameDemo1(同2.2.2)
此处创建DF的方法

可变成如下(完整的写法):

3.4 使用Scala的 class方式创建DataFrame
Man(此处要用到set方法设置属性,所以需要用@BeanProperty)
class Man {
@BeanProperty
var name:String = _
@BeanProperty
var age:Integer = _
@BeanProperty
var fv:Double = _
def this(name: String, age: Int, fv: Double) {
this()
this.name = name
this.age = age
this.fv = fv
}
}
DataFrameDemo2
package com._51doit.spark07 import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession} object DataFrameDemo2 {
def main(args: Array[String]): Unit = {
//编程SparkSQL程序,创建DataFrame
val session: SparkSession = SparkSession.builder()
.appName("SparkSQL2x")
.master("local[*]")
.getOrCreate()
// 获取SparkContext
val sc: SparkContext = session.sparkContext
val rdd: RDD[String] = sc.parallelize(List("小明,18,999.99","老王,35,99.9","小李,25,99.9"))
val manRDD: RDD[Man] = rdd.map(line => {
val fields: Array[String] = line.split(",")
val name: String = fields(0)
val age: Int = fields(1).toInt
val fv: Double = fields(2).toDouble
new Man(name, age, fv)
})
// 创建DataFrame
// import session.implicits._
// manRDD.toDF()
val df: DataFrame = session.createDataFrame(manRDD, classOf[Man])
//建df创建一个视图
df.createTempView("v_boy")
//写SQL
val result: DataFrame = session.sql("SELECT name, fv, age FROM v_boy ORDER BY fv DESC, age ASC")
//触发Action
result.show()
session.stop()
}
}
注意,此处用不了rdd.toDF的形式来创建DataFrame

3.5 使用java的 class方式创建DataFrame
形式和scala的class几乎一样
package com._51doit.spark07 import org.apache.spark.SparkContext
import org.apache.spark.rdd.RDD
import org.apache.spark.sql.{DataFrame, SparkSession} object DataFrameDemo3 {
def main(args: Array[String]): Unit = {
//编程SparkSQL程序,创建DataFrame
val session: SparkSession = SparkSession.builder()
.appName("SparkSQL2x")
.master("local[*]")
.getOrCreate()
// 获取SparkContext
val sc: SparkContext = session.sparkContext
val rdd: RDD[String] = sc.parallelize(List("小明,18,999.99","老王,35,99.9","小李,25,99.9"))
val jPersonRDD: RDD[JPerson] = rdd.map(line => {
val fields: Array[String] = line.split(",")
val name: String = fields(0)
val age: Int = fields(1).toInt
val fv: Double = fields(2).toDouble
new JPerson(name, age, fv)
})
// 创建DateFrame
val df: DataFrame = session.createDataFrame(jPersonRDD, classOf[JPerson])
// 创建一个视图
df.createTempView("v_person")
//写SQL
val result: DataFrame = session.sql("SELECT name, fv, age FROM v_person ORDER BY fv DESC, age ASC") //触发Action
result.show() session.stop()
}
}
3.6 使用scala元组的方式创建DataFrame
创建形式如下:
object DataFrame4 {
def main(args: Array[String]): Unit = {
val session = SparkSession.builder()
.appName("DataFrame4")
.master("local[*]")
.getOrCreate()
// 获取SparkSession
val sc: SparkContext = session.sparkContext
val rdd: RDD[String] = sc.parallelize(List("小明,18,999.99","老王,35,99.9","小李,25,99.9"))
val tpRDD: RDD[(String, Int, Double)] = rdd.map(line => {
val fields: Array[String] = line.split(",")
val n = fields(0)
val a = fields(1)
val f = fields(2)
(n, a.toInt, f.toDouble)
})
// 创建DataFrame
import session.implicits._
val df: DataFrame = tpRDD.toDF
// 使用df创建一个视图
df.createTempView("v_person")
df.printSchema()
}
}
打印结果

这样写想要从表中获取数据是就只能使用_n,非常不方便
简单改变,在DF()方法中加入参数,如下
object DataFrame4 {
def main(args: Array[String]): Unit = {
val session = SparkSession.builder()
.appName("DataFrame4")
.master("local[*]")
.getOrCreate()
// 获取SparkSession
val sc: SparkContext = session.sparkContext
val rdd: RDD[String] = sc.parallelize(List("小明,18,999.99","老王,35,99.9","小李,25,99.9"))
val tpRDD: RDD[(String, Int, Double)] = rdd.map(line => {
val fields: Array[String] = line.split(",")
val n = fields(0)
val a = fields(1)
val f = fields(2)
(n, a.toInt, f.toDouble)
})
// 创建DataFrame
import session.implicits._
val df: DataFrame = tpRDD.toDF("name", "age", "face_value")
df.createTempView("v_person")
val result: DataFrame = session.sql("SELECT name, age, face_value FROM v_person ORDER BY face_value DESC, age ASC")
//触发Action
result.show()
session.stop()
}
3.7 通过row方法的形式创建DataFrame
代码如下
package cn._51doit.spark.day07 import org.apache.spark.rdd.RDD
import org.apache.spark.sql.types.{DoubleType, IntegerType, StringType, StructField, StructType}
import org.apache.spark.sql.{DataFrame, Row, SparkSession} /** *
* 使用SparkSQL的ROW的方式
*/
object DataFrameDemo5 { def main(args: Array[String]): Unit = { //编程SparkSQL程序,创建DataFrame
val session: SparkSession = SparkSession.builder()
.appName("SparkSQL2x")
.master("local[*]")
.getOrCreate() //SparkSession是对SparkContext的封装,里面持有SparkContext的引用
val sc = session.sparkContext val rdd = sc.parallelize(List("laozhao,18,9999.99", "laoduan,30,999.99", "nianhang,28,999.99")) //RowRDD
val rowRDD: RDD[Row] = rdd.map(line => {
val fields = line.split(",")
val n = fields(0)
val a = fields(1)
val f = fields(2)
Row(n, a.toInt, f.toDouble)
}) //schema
// val schema = StructType(
// List(
// StructField("name", StringType),
// StructField("age", IntegerType),
// StructField("fv", DoubleType)
// )
// ) val schema = new StructType()
.add(StructField("name", StringType))
.add(StructField("age", IntegerType))
.add(StructField("fv", DoubleType)) val df: DataFrame = session.createDataFrame(rowRDD, schema)
df.printSchema()
session.stop()
}
}
3.8 通过解析json文件的形式创建DataFrame
package cn._51doit.spark.day07 import org.apache.spark.rdd.RDD
import org.apache.spark.sql.types._
import org.apache.spark.sql.{DataFrame, Row, SparkSession} /** *
* 读取JSON文件创建DataFrame
*/
object DataFrameDemo6 { def main(args: Array[String]): Unit = { //编程SparkSQL程序,创建DataFrame
val spark: SparkSession = SparkSession.builder()
.appName("SparkSQL2x")
.master("local[*]")
.getOrCreate() //从JSON文件中读取数据,并创建DataFrame
//RDD + Schema【json文件中自带Schema】
//
val df: DataFrame = spark.read.json("/Users/star/Desktop/user.json") //df.printSchema()
df.createTempView("v_user")
val result: DataFrame = spark.sql("SELECT name, age, fv FROM v_user WHERE _corrupt_record IS NULL")
result.show()
spark.stop()
}
}
3.9 读取csv文件的形式创建DataFrame
package cn._51doit.spark.day07
import org.apache.spark.sql.{DataFrame, SparkSession}
/** *
* 读取csv文件创建DataFrame
*/
object DataFrameDemo7 {
def main(args: Array[String]): Unit = {
//编程SparkSQL程序,创建DataFrame
val spark: SparkSession = SparkSession.builder()
.appName("SparkSQL2x")
.master("local[*]")
.getOrCreate()
//从JSON文件中读取数据,并创建DataFrame
//RDD + Schema【csv文件中自带Schema】
//
val df: DataFrame = spark.read
.option("header", true) //将第一行当成表头
.option("inferSchema",true) //推断数据的类型,默认都是string
.csv("/Users/star/Desktop/user.csv")
//默认指定名称为 _c0, _c1, _c2
//val df1: DataFrame = df.toDF("name", "age", "fv")
//给指定字段重命名
//val df1 = df.withColumnRenamed("_c0", "name")
df.printSchema()
//df.createTempView("v_user")
//val result: DataFrame = spark.sql("SELECT name, age, fv FROM v_user WHERE _corrupt_record IS NULL")
df.show()
spark.stop()
}
}
4. DSL风格API语法
DSL风格API,就是用编程api的方式,来实现sql语法
使用DSL风格API【就是直接调用DataFrame的算子,Transformation和Action】

DataFrameDSLAPI
object DataFrameDSLAPI {
def main(args: Array[String]): Unit = {
// 编程SparkSQL程序,创建DataFrame
val session: SparkSession = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[*]")
.getOrCreate()
// 获取SparkContext
val sc: SparkContext = session.sparkContext
val rdd: RDD[String] = sc.parallelize(List("A,18,9999.99", "B,30,999.99", "C,28,999.99"))
val boyRDD: RDD[Boy] = rdd.map(line => {
val fields: Array[String] = line.split(",")
val name: String = fields(0)
val age: String = fields(1)
val fv: String = fields(2)
Boy(name, age.toInt, fv.toDouble)
})
// 导入隐式转换,创建DF
import session.implicits._
val df: DataFrame = boyRDD.toDF()
// 使用DSL风格API
val result: Dataset[Row] = df.select("name","fv").where($"fv" >= 1000)
//触发Action
result.show()
session.stop()
}
}
5.案例
wordcount案例
5.1 SQL风格
(1)结合flatmap算子(DSL风格的API,即算子)进行操作
package com._51doit.spark07
import org.apache.spark.sql.{DataFrame, Dataset, SparkSession}
object SQLWordCount {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[*]")
.getOrCreate()
// Dataset是更加智能的RDD,只有一列,命名默认为value
val lines: Dataset[String] = spark.read.textFile("F:/大数据第三阶段/spark/spark-day07/资料/words.txt")
// 导入隐式转换
import spark.implicits._
val words: Dataset[String] = lines.flatMap(_.split(" "))
// 将words注册成视图
words.createTempView("v_words")
// 写SQL
val res: DataFrame = spark.sql("SELECT value word, count(1) counts FROM v_words GROUP BY word ORDER BY counts DESC")
res.write.csv("E:/javafile/out1")
spark.stop()
}
}
(2)直接通过SQL的形式
object SQLWordCountAdv {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[*]")
.getOrCreate()
// Dataset是更加智能的RDD,只有一列,命名默认为value
val lines: Dataset[String] = spark.read.textFile("F:/大数据第三阶段/spark/spark-day07/资料/words.txt")
// 将words注册成视图
lines.createTempView("v_lines")
// 写SQL
spark.sql(
s"""
|SELECT word, COUNT(1) counts FROM
| (SELECT EXPLODE(words) word FROM
| (SELECT SPLIT(value, ' ') words FROM v_lines)
| )
| GROUP BY word ORDER BY counts DESC
|""".stripMargin
).show()
spark.stop()
}
}
5.2 DSL风格(更方便)
(1)
object DataSetWordCount1 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[*]")
.getOrCreate()
// Dataset是更加智能的RDD,只有一列,命名默认为value
val lines: Dataset[String] = spark.read.textFile("F:/大数据第三阶段/spark/spark-day07/资料/words.txt")
// 导入隐式转换
import spark.implicits._
// 调用DSL风格的API
val words: Dataset[String] = lines.flatMap(_.split(" "))
words.groupBy("value")
.count()
.orderBy($"count" desc)
.show()
spark.stop()
}
}
这种写法只能使用默认的列名,若想自己命名列的话可以使用withColumnRenamed,如下

(2)将结果写入数据库(Mysql)
package com._51doit.spark07
import java.util.Properties
import org.apache.spark.sql.{DataFrame, Dataset, SaveMode, SparkSession}
object DataSetWordCount2 {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder()
.appName(this.getClass.getSimpleName)
.master("local[*]")
.getOrCreate()
// Dataset是更加智能的RDD,只有一列,命名默认为value
val lines: Dataset[String] = spark.read.textFile("F:/大数据第三阶段/spark/spark-day07/资料/words.txt")
// 导入隐式转换
import spark.implicits._
// 调用DSL风格的API
val words: DataFrame = lines.flatMap(_.split(" ")).withColumnRenamed("value", "word")
//使用DSL风格的API
//导入agg里面使用的函数
import org.apache.spark.sql.functions._
val result: DataFrame = words.groupBy("word").agg(count("*") as "counts").sort($"counts" desc)
//将数据保存到MySQL
val props = new Properties()
props.setProperty("driver", "com.mysql.jdbc.Driver")
props.setProperty("user", "root")
props.setProperty("password", "feng")
//触发Action
result.write.mode(SaveMode.Append).jdbc("jdbc:mysql://localhost:3306/db_user?characterEncoding=UTF-8&useSSL=true", "words", props)
println("haha")
spark.stop()
}
}
运行结果

大数据学习day24-------spark07-----1. sortBy是Transformation算子,为什么会触发Action 2. SparkSQL 3. DataFrame的创建 4. DSL风格API语法 5 两种风格(SQL、DSL)计算workcount案例的更多相关文章
- 大数据学习day29-----spark09-------1. 练习: 统计店铺按月份的销售额和累计到该月的总销售额(SQL, DSL,RDD) 2. 分组topN的实现(row_number(), rank(), dense_rank()方法的区别)3. spark自定义函数-UDF
1. 练习 数据: (1)需求1:统计有过连续3天以上销售的店铺有哪些,并且计算出连续三天以上的销售额 第一步:将每天的金额求和(同一天可能会有多个订单) SELECT sid,dt,SUM(mone ...
- 大数据学习day21-----spark04------1. 广播变量 2. RDD中的cache 3.RDD的checkpoint方法 4. 计算学科最受欢迎老师TopN
1. 广播变量 1.1 补充知识(来源:https://blog.csdn.net/huashetianzu/article/details/7821674) 之所以存在reduce side jo ...
- 大数据学习系列之四 ----- Hadoop+Hive环境搭建图文详解(单机)
引言 在大数据学习系列之一 ----- Hadoop环境搭建(单机) 成功的搭建了Hadoop的环境,在大数据学习系列之二 ----- HBase环境搭建(单机)成功搭建了HBase的环境以及相关使用 ...
- 大数据学习系列之五 ----- Hive整合HBase图文详解
引言 在上一篇 大数据学习系列之四 ----- Hadoop+Hive环境搭建图文详解(单机) 和之前的大数据学习系列之二 ----- HBase环境搭建(单机) 中成功搭建了Hive和HBase的环 ...
- 大数据学习系列之六 ----- Hadoop+Spark环境搭建
引言 在上一篇中 大数据学习系列之五 ----- Hive整合HBase图文详解 : http://www.panchengming.com/2017/12/18/pancm62/ 中使用Hive整合 ...
- 大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 图文详解
引言 在之前的大数据学习系列中,搭建了Hadoop+Spark+HBase+Hive 环境以及一些测试.其实要说的话,我开始学习大数据的时候,搭建的就是集群,并不是单机模式和伪分布式.至于为什么先写单 ...
- 大数据学习系列之九---- Hive整合Spark和HBase以及相关测试
前言 在之前的大数据学习系列之七 ----- Hadoop+Spark+Zookeeper+HBase+Hive集群搭建 中介绍了集群的环境搭建,但是在使用hive进行数据查询的时候会非常的慢,因为h ...
- 大数据学习之Linux进阶02
大数据学习之Linux进阶 1-> 配置IP 1)修改配置文件 vi /sysconfig/network-scripts/ifcfg-eno16777736 2)注释掉dhcp #BOOTPR ...
- 大数据学习之Linux基础01
大数据学习之Linux基础 01:Linux简介 linux是一种自由和开放源代码的类UNIX操作系统.该操作系统的内核由林纳斯·托瓦兹 在1991年10月5日首次发布.,在加上用户空间的应用程序之后 ...
随机推荐
- 第02课 OpenGL 多边形
你的第一个多边形: 在第一个教程的基础上,我们添加了一个三角形和一个四边形.也许你认为这很简单,但你已经迈出了一大步,要知道任何在OpenGL中绘制的模型都会被分解为这两种简单的图形.读完了这一课,你 ...
- 面试官问我JVM内存结构,我真的是
面试官:今天来聊聊JVM的内存结构吧? 候选者:嗯,好的 候选者:前几次面试的时候也提到了:class文件会被类加载器装载至JVM中,并且JVM会负责程序「运行时」的「内存管理」 候选者:而JVM的内 ...
- FastJson 解析、序列化及反序列化
一.环境准备:使用maven特性在pom.xml中导入fastjson的依赖包 <!-- https://mvnrepository.com/artifact/com.alibaba/fastj ...
- C 数组类型语法总结
数组类型语法总结 数组指针 和 指针数组 区分 数组指针是一个指针,只对应类型的数组.指针数组是一个数组,其中每个元素都是指针 数组指针遵循指针运算法则.指针数组拥有c语言数组的各种特性 数组类型重命 ...
- super和this
super注意点: 1.super调用父类的构造方法,必须在构造方法的第一个 2.super必须只能出现在子类的方法或者构造方法中 3.super和this不能同时调用构造方法 this: 代表的对象 ...
- [第二章]c++学习笔记6(复制构造函数在各个编译器中的表现)
visual studio结果 dev c++结果 两者的输出有所不同 原因:dev c++编译对这个过程进行了优化,因为直接return对象给a,为节省时间所以不生成临时对象,所以结果为10. 注: ...
- silky微服务模块
目录 模块的定义和类型 在模块中注册服务 通过ServiceCollection实现服务注册 通过ContainerBuilder实现服务注册 使用模块初始化任务 使用模块释放资源 模块的依赖关系 构 ...
- [Comet1790]Ternary String Counting
令$f_{i,j,k}$表示前$i$个位置,三种字符最后一次出现的位置为$i,j$和$k$(保证$k<j<i$)的方案数 考虑转移(递推),即分为两步-- 1.填写第$i$个字符,即从$f ...
- [noi34]palindrome
分割实际上就是不断地从两端取出一样的一段,并对剩下的串进行分割.下面我们来证明一下每一次贪心取出最短一段的正确性: 考虑两种分割方式,分别表示成S=A+B+A和S=C+D+C,其中A就是最短的一段,那 ...
- [nowcoder5666B]Infinite Tree
首先考虑由$1!,2!,...,n!$所构成的虚树的一些性质: 1.每一个子树内所包含的阶乘的节点都是一个连续的区间(证明:对于子树k,如果存在$x!$和$y!$,即说明$x!$和$y!$的前$\de ...