SparkSQL之DataFrame 编程(创建DataFrame ,DataFrame数据运算操作 ,输出存储DataFrame)(11)

一  新的编程入口 SparkSession

SparkSession 是 Spark 最新的 SQL 查询起始点 ,实质上是 SQLcontext 和 SparkContext 的组合 ,所以在 SQLContext 和 HIveContext 上可用的 API 在 SparkSession 上同样是可以使用的 . SparkSession 内部封装了 sparkContext ,所以计算实际上是由 sparkContext 完成的 .

object SparkSqlTest {
  def main(args: Array[String]): Unit = {
    --创建SparkSQL 的上下文 ,sparkSession 后续简称为 spark
    val spark: SparkSession = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()

    --SparkSession 就是对SparkContext的封装
    --SparkSession 中持有 SparkContext的引用 ,对 SparkContext 增强了
    --使用 SparkSession 创建DataFrame/DataSet(是对RDD的进一步封装 ,对RDD增强)
    --DataFrame = RDD + Schema(元数据信息,字段名字,字段类型..)
    val sc: SparkContext = spark.sparkContext
    val lines: RDD[String] = sc.textFile(args(0))
    val userRDD: RDD[User] = lines.map(line => {
      val fields = line.split(",")
      val f0 = fields(0)
      val f1 = fields(1).toInt
      val f2 = fields(2).toDouble
      User(f0, f1, f2)
    })
    --导入SparkSession中的隐式转换 ,就可以调用toDF,将RDD转成DataFrame,然后可以使用RDD中的方法
    --这里的spark实际上是 sparkSession 的简称
    import spark.implicits._
    --将userRDD转成 DataFrame
    val userDF: DataFrame = userRDD.toDF
  }
}
case class User(name: String, age: Int, fv: Double)

如果需要 Hive 支持 ,则需要以下创建语句

val spark = SparkSession.builder()
            .appName(this.getClass.getSimpleName)
            .config("spark.some.config.option","some-value")
            .enableHiveSupport()
            .getOrCreate()

二  创建 DataFrames

在Spark SQL 中 SparkSession 是创建 DataFrame 和执行 SQL 的入口, 创建DataFrame的方式有三种

核心要素 : 创建DataFrame ,需要创建 RDD+Schema(元数据信息)

1  从RDD创建DataFrame(从一个已经存在的RDD进行转换)

1)  创建 sparkSession ,后续简称 spark ;

2)  使用 spark 创建原始的 RDD ,对RDD里面的数据进行切割处理 ,将切割处理的数据封装到定义的一个样例类(bean对象)里面 ,返回一个新的 RDD ;

3)  创建 DataFrame 的两种方法 : 

第一种 :  spark 调用 createDataFrame ,将新的RDD 放进去

第二种 : 导入隐式转换(import spark.implicits._) , 然后新的RDD调用 toDF 方法将 RDD 转换成 DataFrame .

注意 : 如果切割处理的数据不封装到 bean对象里面 ,而是直接以 tuple(元组) 的方式返回生成新的RDD ,后续这个RDD转为 DataFrame 之后 ,其 ROW(行)字段的名字就不是元组里面的字段名字 ,框架从tuple元组结构中,对schema的推断,也是成功的,只是字段名是tuple中的数据访问索引。即 row 的描述信息没有被约束

object SparkSqlTest3 {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()

    --创建RDD
    val lines: RDD[String] = spark.sparkContext.parallelize(List("lii,13,90.00", "yuu,14,91.09", "koo,12,90.00"))
    val userRDD: RDD[User2] = lines.map(line => {
      val fields = line.split(",")
      val name = fields(0)
      val age = fields(1).toInt
      val fv = fields(2).toDouble
      User2(name, age, fv)
    })
    --创建DataFrame-----第一种方法
    val userDF: DataFrame = spark.createDataFrame(userRDD)
    userDF.printSchema()
    /**
     * root
     * |-- name: string (nullable = true)
     * |-- age: integer (nullable = false)
     * |-- fv: double (nullable = false)
     */
    userDF.show()
    /**
     * +----+---+-----+
     * |name|age|   fv|
     * +----+---+-----+
     * | lii| 13| 90.0|
     * | yuu| 14|91.09|
     * | koo| 12| 90.0|
     * +----+---+-----+
     */

    --创建DataFrame的----第二种方法--导入隐式转换
    import spark.implicits._
    val userDF2: DataFrame = userRDD.toDF
    userDF2.show()
    /**
     * +----+---+-----+
     * |name|age|   fv|
     * +----+---+-----+
     * | lii| 13| 90.0|
     * | yuu| 14|91.09|
     * | koo| 12| 90.0|
     * +----+---+-----+
     */
  }
}
case class User2(name:String ,age:Int, fv:Double)

利用框架提供的隐式转换可以直接调用toDF创建,并指定字段名(其实就是约束 row 的信息)

object DataFrame03 {
  def main(args: Array[String]): Unit = {
    --创建sparksession
    val session = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()
    --使用 sparksession 创建RDD
    val lines: RDD[String] = session.sparkContext.parallelize(List("huu,12,98.00", "lii,13,99.09", "poo,14,98.09"))
    val rowRDD = lines.map(line => {
      val fields = line.split(",")
      val f0 = fields(0)
      val f1 = fields(1).toInt
      val f2 = fields(2).toDouble
      (f0, f1, f2)
    })
    --创建 DataFrame
    val dataFrame: DataFrame = session.createDataFrame(rowRDD)
    dataFrame.show()    --打印创建的dataF
    --  row 字段信息是元组的索引(字段名是tuple中的数据访问索引)
      +---+---+-----+
      | _1| _2|   _3|
      +---+---+-----+
      |huu| 12| 98.0|
      |lii| 13|99.09|
      |poo| 14|98.09|
      +---+---+-----+     
    --导入隐式转换
    import session.implicits._
    val dataFrame1: DataFrame = rowRDD.toDF("name", "age", "fv")  --对 row 的信息进行约束
    dataFrame1.show()
    --结果如下:
      +----+---+-----+
      |name|age|   fv|
      +----+---+-----+
      | huu| 12| 98.0|
      | lii| 13|99.09|
      | poo| 14|98.09|
      +----+---+-----+    
  }
}

将切割处理的数据封装到Spark系统自定义的Row实例类里面 ,这样就可以给row指定字段属性了 ,创建的RDD跟跟row约束的字段名进行关联

--创建DataFrame = RDD+CaseClass ,然后调用RDD的toDF
--创建DataFrame = RDD+StructType
object DateFrame01 {
  def main(args: Array[String]): Unit = {
    --创建sparkSession ,简称 spark
    val spark: SparkSession = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()

    --创建RDD
    val lines: RDD[String] = spark.sparkContext.parallelize(List("HUU,13,98.0","YII,12,98.99","GRR,17,97.08"))
    --处理数据 ,这个 Row 是spark系统自定义的实例类
    val rowRDD: RDD[Row] = lines.map(line => {
      val fields: Array[String] = line.split(",")
      val f0 = fields(0)
      val f1 = fields(1).toInt
      val f2 = fields(2).toDouble
      Row(f0, f1, f2)
    })
    --对Row的描述信息 ,就是所谓的Schema
    val structType: StructType = StructType(List(
      StructField("name", StringType),          --该字段默认可以为空
      StructField("age", IntegerType, false),     --该字段不可以为空
      StructField("fv", DoubleType, false)
    ))
    --对RDD 和Schema 进行关联
    val df: DataFrame = spark.createDataFrame(rowRDD, structType)
    --创建视图
    df.createTempView("v_user")
    --查询数据
    spark.sql(
      """
        |select name,fv from v_user where age >= 13
        |""".stripMargin).show()
   ----结果如下
      +----+-----+
      |name|   fv|
      +----+-----+
      | HUU| 98.0|
      | GRR|97.08|
      +----+-----+
  }
}

从JSON/Parquet/CSV/JDBC等结构化数据源直接创建DataFrame

2.1  json数据准备

{"name": "laozhao", "age": 18, "fv": 9999.99}
{"name": "laoduan", "age": 28, "fv": 999.99}
{"name": "nianhang", "age": 20, "fv": 999.99}
{"name": "heihei", "age": 20, "fv": 999.99
{"name": "nana", "age": 20, "fv": 999.99, "gender": "male"}
{"name": "test", "height": 180.2}
{"name": "laozhao", "age": 18, "fv": 9999.99}
{"name": "laoduan", "age": 28, "fv": 999.99}
{"name": "nianhang", "age": 20, "fv": 999.99}
{"name": "heihei", "age": 20, "fv": 999.99

2.2  从JSON结构化数据源直接创建DataFrame 的代码实现

object DataFrame04 {
  def main(args: Array[String]): Unit = {
    --创建sparksession ,简称spark
    val spark: SparkSession = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()
    --DataFrame = RDD + Schema
    -- RDD :以后从哪里读取数据
    --Schema : 从 json 中获取信息
    val dataFrame: DataFrame = spark.read.json(args(0))
    val dataFrame1 = spark.read.format("json").load(args(0))
    dataFrame1.printSchema()
    /**
     * root
     * |-- _corrupt_record: string (nullable = true)
     * |-- age: long (nullable = true)
     * |-- fv: double (nullable = true)
     * |-- gender: string (nullable = true)
     * |-- height: double (nullable = true)
     * |-- name: string (nullable = true)
     */
    dataFrame1.show()
    /**
     * +--------------------+----+-------+------+------+--------+
     * |     _corrupt_record| age|     fv|gender|height|    name|
     * +--------------------+----+-------+------+------+--------+
     * |                null|  18|9999.99|  null|  null| laozhao|
     * |                null|  28| 999.99|  null|  null| laoduan|
     * |                null|  20| 999.99|  null|  null|nianhang|
     * |{"name": "heihei"...|null|   null|  null|  null|    null|
     * |                null|  20| 999.99|  male|  null|    nana|
     * |                null|null|   null|  null| 180.2|    test|
     * |                null|  18|9999.99|  null|  null| laozhao|
     * |                null|  28| 999.99|  null|  null| laoduan|
     * |                null|  20| 999.99|  null|  null|nianhang|
     * |{"name": "heihei"...|null|   null|  null|  null|    null|
     * +--------------------+----+-------+------+------+--------+
     */
  }
}

2.2.1  将查询的数据信息存储到 mysql 里面 (注意 : 代码运行前需要将 mysql 下的数据库处于连接的状态 )

    --创建视图
    dataFrame1.createTempView("v_df")

    --有问题的数据 _corrupt_record列不为null ,
    --将正常的数据写到 mysql 里面
    val df2: DataFrame = spark.sql(
      """
        |select name,age,fv from v_df where _corrupt_record IS NULL
        |""".stripMargin)
    --定义一个配置对象
    val properties = new Properties()
    --设置用户名
    properties.setProperty("user","root") 
    --设置密码 
    properties.setProperty("password","123456")
    --将查询到的信息写到 mysql里面
    df2.write.mode(SaveMode.Append).jdbc("jdbc:mysql://localhost:3306/db_data?characterEncoding=utf-8","t_user",properties)
 

2.2.2  mysql 里面写入的查询信息(存储的表格是系统自动创建的)

2.3  从CSV结构化数据源直接创建DataFrame

object CreateDataFrameFromCSV {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()
    ---------------创建DataFrame = RDD+Schema---------------------
    ---------第一种方式--------没有指定表头---------------------
    --csv中没有schema ,读取csv文件中可以知道有多少列 ,每列都可以有默认的名称
    val dataFrame = spark.read.csv(args(0))
    dataFrame.toDF("name","age","fv")

    --------第二种方式--------指定第一列为表头(第一列的数据为字段名字)--------------
    val df: DataFrame = spark.read
      .option("header", "true")
      .option("inferSchema", "true")
      .csv(args(0))

    -----------第三种方式-----最佳方式-------------------
    --对ROW的描述信息,就是所谓的Schema
    val structType = StructType(List(
      StructField("name", DataTypes.StringType),
      StructField("age", IntegerType, false),
      StructField("fv", DoubleType, false)
    ))
    --指定表头,然后与描述信息相关联
    val dataFrame1: DataFrame = spark.read.option("header", "true").schema(structType).csv(args(0))
    dataFrame1.printSchema()
    dataFrame1.show()
  }
}

2.4  从Parquet结构化数据源直接创建DataFrame

object CreateDataFrameFromParquet01 {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()
    --Parquet文件自带Schema ,读取parquet格式的文件,parquet文件默认使用的是snappy压缩,所以该文件是二进制形式存储的列式存储文件
    val df: DataFrame = spark.read.parquet("d://sparkout")
    df.printSchema()
    /**
     * root
     * |-- name: string (nullable = true)
     * |-- age: integer (nullable = true)
     * |-- fv: double (nullable = true)
     */
    df.show()
    /**
     * +----+---+-----+
     * |name|age|   fv|
     * +----+---+-----+
     * | HUU| 13| 98.0|
     * | YII| 12|98.99|
     * | GRR| 17|97.08|
     * +----+---+-----+
     */
    ---因为parquet 是列式存储的文件 ,所以可以根据需求 ,有选择性的读取数据
      df.select("age").show()
    /**
     * +---+
     * |age|
     * +---+
     * | 13|
     * | 12|
     * | 17|
     * +---+
     */
  }
}

2.4  从JDBC结构化数据源直接创建DataFrame

object CreateDataFrameFromJDBC {

  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()
    --定义一个配置对象 ,将mysql用户名和密码封装起来
    val props = new Properties()
    props.setProperty("user","root")
    props.setProperty("password","123456")
    --读取mysql里面指定的表里面的数据生成 dataFrame, 保证数据库是打开连接的状态
    val dataFrame = spark.read.jdbc("jdbc:mysql://localhost:3306/db_data?characterEncoding=utf-8", "t_user", props)
    dataFrame.printSchema()
    /**
     * root
     * |-- name: string (nullable = true)
     * |-- age: long (nullable = true)
     * |-- fv: double (nullable = true)
     */
    dataFrame.show()
    /**
     * +--------+----+-------+
     * |    name| age|     fv|
     * +--------+----+-------+
     * | laozhao|  18|9999.99|
     * | laoduan|  28| 999.99|
     * |nianhang|  20| 999.99|
     * |    nana|  20| 999.99|
     * |    test|null|   null|
     * | laozhao|  18|9999.99|
     * | laoduan|  28| 999.99|
     * |nianhang|  20| 999.99|
     * +--------+----+-------+
     */
  }
}

3  从外部服务器读取数据创建DataFrame(从Hive Table进行查询)

三  DataFrame 数据运算操作

四  输出存储DataFrame

1  将数据以 parquet 的形式输出存储到 windows 的 d 盘

parquet 是列式存储型的文件 ,在读取文件时可以选择性的读取

object SaveToParquet01 {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName(this.getClass.getSimpleName)
      .master("local[*]")
      .getOrCreate()
    val lines: RDD[String] = spark.sparkContext.parallelize(List("HUU,13,98.0","YII,12,98.99","GRR,17,97.08"))
    --这里的 Row 是系统定义的一个类
    val rowRDD: RDD[Row] = lines.map(line => {
      val fields = line.split(",")
      val f0 = fields(0)
      val f1 = fields(1).toInt
      val f2 = fields(2).toDouble
      Row(f0, f1, f2)
    })
    --创建DataFrame的第一种方法------导入隐式转换-----------
    import spark.implicits._
    val df: DataFrame = rowRDD.toDF("name","age","fv")
    df.createTempView("v_user")
    --查询
    spark.sql(
      """
        |select name,fv from v_user where age >= 13
        |""".stripMargin).show()
    /**
     * +----+-----+
     * |name|   fv|
     * +----+-----+
     * | HUU| 98.0|
     * | GRR|97.08|
     * +----+-----+
     */
    -----创建DataFrame的第二种方法------StructType与RDD关联,调用createDataFrame方法-------
    ----对 ROW 的描述信息,就是所谓的Schema
    val structType = StructType(List(
      StructField("name", DataTypes.StringType),
      StructField("age", IntegerType, false),
      StructField("fv", DoubleType, false)
    ))
    ---将RDD和Schema进行关联
    val dataFrame: DataFrame = spark.createDataFrame(rowRDD, structType)
    dataFrame.repartition(2).write.mode(SaveMode.Append).parquet("d://sparkout")
    dataFrame.write.parquet("d://sparkout1")
    spark.stop()
  }
}

 1.1  dataFrame.repartition(2).write.mode(SaveMode.Append).parquet("d://sparkout")  --


 1.2  dataFrame.write.parquet("d://sparkout1")

2  将DataFrame 存储到 mysql 里面

    --创建视图
    dataFrame1.createTempView("v_df")

    --有问题的数据 _corrupt_record列不为null ,
    --将正常的数据写到 mysql 里面
    val df2: DataFrame = spark.sql(
      """
        |select name,age,fv from v_df where _corrupt_record IS NULL
        |""".stripMargin)
    --定义一个配置对象
    val properties = new Properties()
    --设置用户名
    properties.setProperty("user","root") 
    --设置密码 
    properties.setProperty("password","123456")
    --将查询到的信息写到 mysql里面      参数二 : 表名(存储查询数据的表格的名字)
    df2.write.mode(SaveMode.Append).jdbc("jdbc:mysql://localhost:3306/db_data?characterEncoding=utf-8","t_user",properties)
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值