Spark 之 SQL 学习笔记

目录

概述

为什么学习Spark SQL:

Spark SQL的版本迭代

SparkSession

sparkSession概念解释:

特点

创建SparkSession

在spark-shell中创建

在IDEA中创建SparkSession

RDD,DataFrame 和 DataSet

RDD的局限性

什么是DataFrame

特点

DataFrame解释

DataFrame编程

DataSet

为什么产生DataSet

解释

为什么需要 DataFrame 和 DataSet

Spark SQL 程序编写步骤

创建DataFrame

DataFrame常用操作

DSL风格语法

SQL风格语法

DataFrame 支持的操作

以编程方式执行Spark SQL

编写 Spark SQL 查询程序

提交Spark任务

数据源

通用的load和save功能

Save Model

JDBC

从 MySQL 中加载数据(Spark Shell 方式)

将数据写入 MySQL 中(Spark Submit 方式)

JSON

Parquet Files

Spark On Yarn

Spark 整合 Hive

SparkSQL自定义聚合函数

SparkSQL 定义普通函数 

定义 SparkSQL 的自定义聚集函数 

SparkSQL 常用窗口分析函数


概述

(版本:Spark 2.3.2)

Spark SQL 是 Spark 用来处理结构化数据(结构化数据可以来自外部结构化数据源也可以通过 RDD 获取)的一个模块,它提供了一个编程抽象叫做 DataFrame 并且作为分布式 SQL 查询引擎的作用。

外部的结构化数据源包括 JSON、Parquet(默认)、RMDBS、Hive 等。当前 Spark SQL 使用 Catalyst优化器来对 SQL 进行优化,从而得到更加高效的执行方案。并且可以将结果存储到外部系统。


为什么学习Spark SQL:

  • 首先

我们已经学习了 Hive,它是将 Hive SQL 转换成 MapReduce 然后提交到集群上执行,大大简化了编写 MapReduce 的程序的复杂性,由于 MapReduce 这种计算模型执行效率比较慢。所以 Spark SQL 就应运而生,它的工作机制是将 Spark SQL 的 SQL 查询转换成 Spark Core 的应用程序,然后提交到集群执行,执行效率非常快!

  • 其次,SparkSQL的特点
  1. 容易整合
  2. 统一的数据访问格式
  3. 兼容Hive
  4. 标准的数据连接

Spark SQL的版本迭代

  1. SparkSQL 的前身是 Shark。由于 Shark 自身的不完善,2014 年 6 月 1 日 Reynold Xin 宣布:停止对 Shark 的开发。SparkSQL 抛弃原有 Shark 的代码,汲取了 Shark 的一些优点,如内存列存储(In-Memory Columnar Storage)、Hive 兼容性等,重新开发 SparkSQL。
  2. Spark-1.1:2014 年 9 月 11 日,发布 Spark1.1.0。Spark 从 1.0 开始引入 SparkSQL(Shark不再支持升级与维护)。Spark1.1.0 变化较大是 SparkSQL 和 MLlib
  3. Spark-1.3:增加 DataFrame 新 API
  4. Spark-1.4:增加窗口分析函数
  5. Spark 1.5:钨丝计划。Hive 中有 UDF 与 UDAF,Spark 中对 UDF 支持较早UDAF:User Defined Aggregate Function 用户自定义聚合函数,直到 Spark 1.5.x 才引入的最新特性
  6. spark-1.6:执行的 sql 中可以增加"--"注释,Spark-1.5/1.6 的新特性,引入 DataSet 的概念
  7. Spark-2.x:SparkSQL+DataFrame+DataSet(正式版本),Structured Streaming(DataSet),引入SparkSession 统一了 RDD,DataFrame,DataSet 的编程入口

SparkSession

sparkSession概念解释:

SparkSession 是 Spark-2.0 引如的新概念。SparkSession 为用户提供了统一的切入点,来让用户学习 Spark 的各项功能。在 Spark 的早期版本中,SparkContext 是 Spark 的主要切入点,由于 RDD 是主要的 API,我们通过 sparkContext 来创建和操作 RDD。对于每个其他的 API,我们需要使用不同的 context。

例如:
对于 Spark Streaming,我们需要使用 StreamingContext
对于 Spark SQL,使用 SQLContext
对于 Hive,使用 HiveContext

但是随着 DataSet 和 DataFrame 的 API 逐渐成为标准的 API,就需要为他们建立接入点。所以在 Spark2.0 中,引入SparkSession 作为 DataSet 和 DataFrame API 的切入点,SparkSession封装了 SparkConf、SparkContext 和 SQLContext。为了向后兼容,SQLContext 和 HiveCont也被保存下来。SparkSession 实质上是 SQLContext 和 HiveContext 的组合,所以在 SQLContext 和 HiveContext上可用的 API 在 SparkSession 上同样是可以使用的。SparkSession 内部封装了 SparkContext,所以计算实际上是由 SparkContext 完成的。

特点

  1. 为用户提供一个统一的切入点使用 Spark 各项功能
  2. 允许用户通过它调用 DataFrame 和 Dataset 相关 API 来编写程序
  3. 减少了用户需要了解的一些概念,可以很容易的与 Spark 进行交互
  4. 与 Spark 交互之时不需要显示的创建 SparkConf、SparkContext 以及 SQlContext,这些对象已经封闭在 SparkSession 中
  5. SparkSession 提供对 Hive 特征的内部支持:用 HiveQL 写 SQL 语句,访问 Hive UDFs,从Hive 表中读取数据。

创建SparkSession

在spark-shell中创建

[hadoop@hadoop02 ~]$ ~/apps/spark-2.3.1-bin-hadoop2.7/bin/spark-shell \
> --master spark://hadoop02:7077 \
> --executor-memory 512m \
> --total-executor-cores 1

SparkSession 会被自动初始化一个对象叫做 spark,为了向后兼容,Spark-Shell 还提供了一个sparkContext 的初始化对象,方便用户操作:

在IDEA中创建SparkSession

RDD,DataFrame 和 DataSet

RDD的局限性

RDD 仅表示数据集,RDD 没有元数据,也就是说没有字段语义定义。它需要用户自己优化程序,对程序员要求较高,从不同数据源读取数据相对困难,读取到不同格式的数据都必须用户自己定义转换方式合并多个数据源中的数据也较困难。

SparkCore的RDD编程

1)首先要找到程序入口(SparkContext)
2)通过程序入口构建一个 RDD(核心的抽象 RDD)
3)对写 RDD 进行 Transformation 或者 Action 的操作
4)对最后的结果进行处理(输出或者存入数据库等)

什么是DataFrame

由于 RDD 的局限性,Spark 产生了 DataFrame,其中 Schema 是就是元数据,是语义描述信息。在 Spark1.3 之前,DataFrame 被称为SchemaRDD。以行为单位构成的分布式数据集合,按照列赋予不同的名称。对 select、fileter、aggregation 和 sort 等操作符的抽象。

DataFrame = RDD+Schema = SchemaRDD

特点

  1. 内部数据无类型,统一为 Row
  2. DataFrame 是一种特殊类型的 Dataset,DataSet[Row] = DataFrame
  3. DataFrame 自带优化器 Catalyst,可以自动优化程序
  4. DataFrame 提供了一整套的 Data Source API

与 RDD 类似,DataFrame 也是一个分布式数据容器。然而 DataFrame 更像传统数据库的二维表格,除了数据以外,还记录数据的结构信息,即 Schema。同时,与 Hive 类似,DataFrame也支持嵌套数据类型(struct、array 和 map)。从 API 易用性的角度上 看,DataFrame API提供的是一套高层的关系操作,比函数式的 RDD API 

DataFrame解释

A DataFrame is a Dataset organized into named columns. It is conceptually equivalent to a table in a relational database or a data frame in R/Python, but with richer optimizations under the hood. DataFrames can be constructed from a wide array of sources such as: structured data files, tables in Hive, external databases, or existing RDDs. The DataFrame API is available in Scala, Java, Python, and R. In Scala and Java, a DataFrame is represented by a Dataset of Rows. In the Scala APIDataFrameis simply a type alias of Dataset[Row]. While, in Java API, users need to use Dataset<Row> to represent a DataFrame.

翻译:

DataFrame 是按列名的方式去组织的一个分布式的数据集(RDD),就像关系型数据库里面的一张表,(或者说好比是 R/Python 语言里面的 DataFrame),不过 SparkSQL 这儿的方法比 R/Python 语言里面的 DataFrame 提供的操作方法更丰富,DataFrame 的数据源有如下:结构化的文件,Hive 里面的表,外部的数据库(MySQL 等),已经存在的 RDD。DataFrame 提供了 Scala,Java,Python,R 的编程 API,在 Scala 或者 Java 编程中,一个 DataFrame 表示以行组织的 Rows 的数据集合,在 Scala 的 API 中,DataFrame 就可以看做是 Dataset[Row]的另一种称呼,但是,在 Java 的 API 中,开发者必须使用 Dataset<Row>去表示一个 DataFrame。

DataFrame编程

Spark SQL 编程:

  1. 首先要找到程序入口(SQLContext),新版本 Spark-2.x 之后寻找 SparkSession
  2. 通过程序入口构建一个 DataFrame(核心的抽象 DataFrame)
  3. 对 DataFrame 做各种操作。最重要就是编写 SQL 语句
  4. 对得到的结果数据进行处理(打印输出或者存入数据库等)

DataSet

为什么产生DataSet

由于 DataFrame 的数据类型统一是 Row,所以 DataFrame 也是有缺点的。Row 运行时类型检查,比如 salary 是字符串类型,下面语句也只有运行时才进行类型检查。所以,Spark SQL 引入了 Dataset,扩展了 DataFrame API,提供了编译时类型检查,面向对象风格的 API。但是Dataset 可以和 DataFrame、RDD 相互转换。DataFrame=Dataset[Row],可见 DataFrame 是一种特殊的 Dataset。

dataframe.filter("salary>1000").show()

解释

A Dataset is a distributed collection of data. Dataset is a new interface added in Spark 1.6 that provides the benefits of RDDs (strong typing, ability to use powerful lambda functions) with the benefits of Spark SQL’s optimized execution engine. A Dataset can be constructed from JVM objects and then manipulated using functional transformations (map, flatMap, filter, etc.). The Dataset API is available in Scala and Java. Python does not have the support for the Dataset API. But due to Python’s dynamic nature, many of the benefits of the Dataset API are already available (i.e. you can access the field of a row by name naturally row.columnName). The case for R is similar.

翻译:

一个 Dataset 是一个分布式的数据集合 Dataset 是在 Spark 1.6 中被添加的新接口,它提供了RDD 的优点(强类型化,能够使用强大的 lambda 函数)与 Spark SQL 执行引擎的优点。一个 Dataset 可以从 JVM 对象来构造并且使用转换功能(map, flatMap, filter,等等)。Dataset API 在 Scala 和 Java 是可用的。Python 不支持 Dataset API。但是由于 Python 的动态特性,许多Dataset API 的优点已经可用了 (也就是说,你可能通过 name 天生的 row.columnName 属性访问一行中的字段)。这种情况和 R 相似。

为什么需要 DataFrame 和 DataSet

Spark SQL提供了两种方式读取操作数据:1. SQL 查询   2. DataFrame 和 Dataset API。但是,SQL 语句虽然简单,但是 SQL 的表达能力却是有限的,DataFrame 和 Dataset 可以采用更加通用的语言(Scala 或 Python)来表达用户的查询请求。此外,Dataset 可以更快捕捉错误,因为 SQL 是运行时捕获异常,而 Dataset 是编译时检查错误。

Spark SQL 程序编写步骤

  1. 创建 SparkSession 对象
  2. 创建 DataFrame 或 Dataset
  3. 在 DataFrame 或 Dataset 之上进行转换和 Action
  4. 返回结果(保存结果到 HDFS 中,或直接打印出来)

创建DataFrame

数据文件 :

  1. 在本地创建一个文件,有五列,分别是 id、name、sex、age、department,用逗号分隔,然后上传到 HDFS 上:hdfs dfs -put student.txt /student
  2. 在 spark shell 执行下面命令,读取数据,将每一行的数据使用列分隔符分割:val lineRDD = sc.textFile("hdfs://myha01/student/student.txt").map(_.split(","))
  3. 定义 case class(相当于表的 schema):case class Student(id:Int, name:String, sex:String, age:Int, department:String)
  4. 将 RDD 和 case class 关联:val studentRDD = lineRDD.map(x => Student(x(0).toInt, x(1), x(2), x(3).toInt, x(4)))
  5. 将 RDD 转换成 DataFrame:Spark-2.3 : val studentDF = spark.createDataFrame(studentRDD) 或者 Spark-1.6 : val studentDF = studentRDD.toDF
  6. 对 DataFrame 进行处理:studentDF.show 或者 studentDF.printSchema

DataFrame常用操作

DSL风格语法

//打印 DataFrame 的 Schema 信息
studentDF.printSchema

DSL风格语法示例:

//查看 DataFrame 部分列中的内容
studentDF.select("name", "age").show
studentDF.select(col("name"), col("age")).show
studentDF.select(studentDF.col("name"), studentDF.col("age")).show

//查询所有的 name 和 age,并将 age+1
studentDF.select(col("id"), col("name"), col("age") + 1).show
studentDF.select(studentDF ("id"), studentDF ("name"), studentDF ("age") + 1).show


// 按年龄进行分组并统计相同年龄的人数
studentDF.groupBy("age").count().show()

SQL风格语法

注:如果想使用 SQL 风格的语法,需要将 DataFrame 注册成表

老版本写法:

  • studentDF.registerTempTable("t_student")

新版本写法:

  1. Session 范围内的临时表:studentDF.createOrReplaceTempView(“t_student”)只在Session范围内有效,Session 结束临时表自动销毁
  2. 全局范围内的临时表:studentDF.createGlobalTempView(“t_student”)所有 Session 共享

SQL风格语法示例:

// 查询年龄最大的前五名
sqlContext.sql("select * from t_student order by age desc limit 5").show

// 显示表的 Schema 信息
sqlContext.sql("desc t_student ").show

// 统计学生数超过 6 个的部门和该部门的学生人数。并且按照学生的个数降序排序
sqlContext.sql("select department, count(*) as total from t_student group by department having total > 6 order by total desc").show

DataFrame 支持的操作

以编程方式执行Spark SQL

编写 Spark SQL 查询程序

package com.mazh.spark.sql
import org.apache.spark.sql.{SQLContext, SparkSession}
import org.apache.spark.{SparkConf, SparkContext}

//case class 一定要事先放到外面定义好
case class Student(id: Int, name: String, sex: String, age: Int, department: String)
object StudentSparkSQL {
     def main(args: Array[String]) {
         //创建 SparkConf()并设置 App 名称
         val conf = new SparkConf().setAppName("FirstSparkSQLAPP--Student")
         //SQLContext 要依赖 SparkContext
         val sc = new SparkContext(conf)
         //创建 SQLContext
         val sqlContext = new SQLContext(sc)
         //从指定的地址创建 RDD
         val lineRDD = sc.textFile(args(0)).map(_.split(","))
         //创建 case class
         //将 RDD 和 case class 关联
         val studentRDD = lineRDD.map(x => Student(x(0).toInt, x(1), x(2), x(3).toInt,x(4)))
         //导入隐式转换,如果不导入无法将 RDD 转换成 DataFrame
         //将 RDD 转换成 DataFrame
         import sqlContext.implicits._
         val studentDF = studentRDD.toDF
         //注册表
         studentDF.registerTempTable("t_student")
         //传入 SQL
         val df = sqlContext.sql("select department, count(*) as total from t_student group by department having total > 6 order by total desc")
         //将结果以 JSON 的方式存储到指定位置
         df.write.json(args(1))
         //停止 Spark Context
         sc.stop()
    }
}

提交Spark任务

$SPARK_HOME/bin/spark-submit \
--class com.mazh.spark.sql.StudentSparkSQL \
--master spark://hadoop02:7077,hadoop04:7077 \
/home/hadoop/Spark_SQL-1.0-SNAPSHOT.jar \
hdfs://myha01/student/student.txt \
hdfs://myha01/student/output_sparksql

数据源

通用的load和save功能

编写普通的 load 和 save 功能

spark.read.load("hdfs://myha01/spark/sql/input/users.parquet").select("name","favorite_color").write.save("hdfs://myha01/spark/sql/output")

指定 load 和 save 的特定文件格式

spark.read.format("json").load("hdfs://myha01/spark/sql/input/people.json").select("name", "age").write.format("csv").save("hdfs://myha01/spark/sql/csv")

Save Model

JDBC

Spark SQL 可以通过 JDBC 从关系型数据库中读取数据的方式创建 DataFrame,通过对 DataFrame 一系列的计算后,还可以将数据再写回关系型数据库中。

从 MySQL 中加载数据(Spark Shell 方式)

启动 Spark Shell,必须指定 mysql 连接驱动 jar 包

启动本机的单进程 Shell:
$SPARK_HOME/bin/spark-shell \
--jars $SPARK_HOME/mysql-connector-java-5.1.40-bin.jar \
--driver-class-path $SPARK_HOME/mysql-connector-java-5.1.40-bin.jar 

启动连接 Spark 集群的 Shell:
$SPARK_HOME/bin/spark-shell \
--master spark://hadoop02:7077,hadoop04:7077 \
--jars $SPARK_HOME/mysql-connector-java-5.1.40-bin.jar \
--driver-class-path $SPARK_HOME/mysql-connector-java-5.1.40-bin.jar 

从 mysql 中加载数据

val jdbcDF = sqlContext.read.format("jdbc").options(Map("url" -> "jdbc:mysql://hadoop02:3306/spider", "driver" -> "com.mysql.jdbc.Driver", "dbtable" -> "lagou", "user" -> "root", "password" -> "root")).load()

将数据写入 MySQL 中(Spark Submit 方式)

package com.mazh.spark.sql

import java.util.Properties
import org.apache.spark.sql.types.{IntegerType, StringType, StructField,StructType}
import org.apache.spark.sql.{Row, SQLContext}
import org.apache.spark.{SparkConf, SparkContext}

object SparkSQL_JDBC {
    def main(args: Array[String]) {
        val conf = new SparkConf().setAppName("SparkSQL_JDBC")
        val sc = new SparkContext(conf)
        val sqlContext = new SQLContext(sc)
        //通过并行化创建 RDD
        // val studentRDD = sc.parallelize(Array("1 huangbo 33", "2 xuzheng 44", "3 wangbaoqiang 55")).map(_.split(" "))
        //通过读取文件创建 RDD
        val studentRDD = sc.textFile(args(0)).map(_.split(","))
        //通过 StructType 直接指定每个字段的 schema
        val schema = StructType(
            List(
                 StructField("id", IntegerType, true),
                 StructField("name", StringType, true),
                 StructField("sex", StringType, true),
                 StructField("age", IntegerType, true),
                 StructField("department", StringType, true)
            )
        )
        //将 RDD 映射到 rowRDD
        val rowRDD = studentRDD.map(p => Row(p(0).toInt, p(1).trim, p(2).trim,p(3).toInt,p(4).trim))
        //将 schema 信息应用到 rowRDD 上
        val studentDataFrame = sqlContext.createDataFrame(rowRDD, schema)
        //创建 Properties 存储数据库相关属性
        val prop = new Properties()
        prop.put("user", "root")
        prop.put("password", "root")
        //将数据追加到数据库
        studentDataFrame.write.mode("append").jdbc("jdbc:mysql://hadoop02:3306/spider","student", prop)
        //停止 SparkContext
        sc.stop()
    }
}

准备数据:student.txt 存储在 HDFS 上的/student 目录中
给项目打成 jar 包,上传到客户端
提交任务给 Spark 集群:

$SPARK_HOME/bin/spark-submit \
--class com.mazh.spark.sql.SparkSQL_JDBC \
--master spark://hadoop02:7077,hadoop04:7077 \
--jars $SPARK_HOME/mysql-connector-java-5.1.40-bin.jar \
--driver-class-path $SPARK_HOME/mysql-connector-java-5.1.40-bin.jar \
/home/hadoop/Spark_WordCount-1.0-SNAPSHOT.jar \
hdfs://myha01/student/student.txt

结果展示:

JSON

代码:

object TestSparkSQL_ReadJSON {
    def main(args: Array[String]): Unit = {
        // 构建 SparkSQL 程序的编程入口对象 SparkSession
        val sparkSession:SparkSession = SparkSession.builder()
          .appName("MyFirstSparkSQL")
          .config("someKey", "someValue")
          .master("local")
          .getOrCreate()

         // 方式 1
         val df1 = sparkSession.read.json("D:\\bigdata\\json\\people.json")

         // 方式 2
         val df2 = sparkSession.read.format("json").load("D:\\bigdata\\json\\people.json")
    }
}

Parquet Files

代码:

object TestSparkSQL_ReadParquet {
    def main(args: Array[String]): Unit = {
        // 构建 SparkSQL 程序的编程入口对象 SparkSession
        val sparkSession:SparkSession = SparkSession.builder()
          .appName("MyFirstSparkSQL")
          .config("someKey", "someValue")
          .master("local")
          .getOrCreate()

          // 方式 1
          val df1 = sparkSession.read.parquet("D:\\bigdata\\parquet\\people.parquet")

          // 方式 2
          val df2 = sparkSession.read.format("parquet").load("D:\\bigdata\\json\\people.json")
    }
}

Spark On Yarn

参照博客:https://blog.csdn.net/Jerry_991/article/details/85042305

Spark 整合 Hive

参照博客:https://blog.csdn.net/Jerry_991/article/details/84000097

SparkSQL自定义聚合函数

SparkSQL 定义普通函数 

要点:spark.udf.register(“function_name”, function)

 /*
  *    第一步:获取程序入口
  */
    val sparkConf = new SparkConf()
    sparkConf.setAppName("SparkSQL_UAF_Length").setMaster("local")
    val sparkContext = new SparkContext(sparkConf)
    val sqlContext = new SQLContext(sparkContext)

 /*
  *    第二步:获取到一个DataFrame,然后注册为一张表
  *    
  *    JDBC:三个参数
  *    url:String
  *    table:String
  *    properties:Properties
  */ 
    val url = "jdbc:mysql://hadoop02:3306/bigdata"
    val table = "student"
    val properties = new Properties()
    properties.put("user","root")
    properties.put("password","root")
    val studentDF:DataFrame = sqlContext.read.jdbc(url,table,properties)
    
  /*
   *    第三步:把这个dataFrame注册为一张临时表
   */
    studentDF.createTempView("student")

  /*
   *    第四步:定义一个函数
   */  
    sqlContext.udf.register("strLength",(x:String) => x.length)

  /*
   *    第五步:使用这个函数做一个操作,求出某个字段的长度
   */
    sqlContext.sql("select strLength(name) as name_len from student").show()
    
  /*
   *    第六步:程序完结,关闭资源
   */
    sparkContext.stop()
    
     
    

定义 SparkSQL 的自定义聚集函数 

要点:Class MyUDAF extends UserDefinedAggregationFunction,spark.udf.register("function_name", function)

object SparkSQL_UDAF_AvgAge extends UserDefinedAggregateFunction{
    /**
     * 定义输入的数据的类型
     */
     override def inputSchema: StructType = StructType(
         StructField("age", DoubleType, true) :: Nil
     )

    /**
     * 定义辅助字段:
     *
     * 1、辅助字段 1:用来记录所有年龄之和 total
     * 2、辅助字段 2:用来总记录所有学生的个数 count
     */
     override def bufferSchema: StructType = StructType(
         StructField("total", DoubleType, true)::
         StructField("count", IntegerType, true)::
         Nil
     )

     /**
     * 计算学生的平均年龄 计算公式: 学生年龄的总和 / 学生总数
     *
     * 所以要初始化要两个辅助字段:
     * total : 0.0
     * count : 0
     */
     override def initialize(buffer: MutableAggregationBuffer): Unit = {
         buffer.update(0, 0.0)
         buffer.update(1, 0)
     }

    /**
     * 每次给一条记录, 然后进行累加。进行累加变量 buffer 的状态更新
     * 这是一个局部操作。
     */
     override def update(buffer: MutableAggregationBuffer, input: Row): Unit = {
         val lastTotal = buffer.getDouble(0)
         val lastCount = buffer.getInt(1)
         val currentSalary = input.getDouble(0)
         buffer.update(0,lastTotal + currentSalary)
         buffer.update(1,lastCount+1)
     }

    /**
     * 当局部操作完成,最后需要一个全局合并的操作
     * 就相当于是 reducer 阶段的最终合并
     */
     override def merge(buffer1: MutableAggregationBuffer, buffer2: Row): Unit = {
         val total1 = buffer1.getDouble(0)
         val count1 = buffer1.getInt(1)
         val total2 = buffer2.getDouble(0)
         val count2 = buffer2.getInt(1)
         buffer1.update(0, total1 + total2)
         buffer1.update(1, count1 + count2)
     }

    /**
     * 计算平均年龄
     */
     override def evaluate(buffer: Row): Any = {
         val total = buffer.getDouble(0)
         val count = buffer.getInt(1)
         total / count
     }

     /**
      * 返回结果数据类型
      */
     override def dataType: DataType = DoubleType

    /**
     * 输入和输出的字段类型是否匹配。也即是否一致
     */
     override def deterministic: Boolean = true
}

使用测试:

 

SparkSQL 常用窗口分析函数

(待整理)

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页