[2.4]以row_number为例解读spark sql的窗口函数

参考

spark官网
王家林DT大数据梦工厂

场景

将本地文件toNGroup.txt中的内容:

hadoop@master:~/resource$ cat toNGroup.txt 
hadoop 29
hadoop 87
hadoop 39
hadoop 27
hadoop 88
spark 29
spark 90
spark 27
spark 84
spark 92
hadoop@master:~/resource$ 

按照第一个字段分组,然后按照第二个字段降序排序,取前4位。即正常结果如下显示:

spark   92
spark   90
spark   84
spark   29
hadoop  88
hadoop  87
hadoop  39
hadoop  29

分析

  • 将本地数据导入到hive表中。spark SQL 通过HiveContext可以直接操作 hive仓库表中的数据
  • 通过窗口函数生成一个数字序列,取该序列的前4条数据即可
    spark sql中提供了很多内置的函数,这个与mysql中内置的函数类型相似,大致分为:
    Aggregate functions、Collection functions、Date time functions、Math functions、String functions、UDF functions以及Window functions - 具体内容可以参看[参考]中的相关链接。通过这些函数,数据分析人员可以很方便的对数据进行各种丰富的挖掘。本文主要以row_number函数实现分组排序为例子,体验窗口函数的使用。row_number函数说明如下:
def row_number(): Column

Window function: returns a sequential number starting at 1 within a window partition. 

嘿,分组、排序在各大电商网站的应用是有多常见啊!

实验

package main.scala

import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.hive.HiveContext

/**
 * 窗口函数实战
 * 
 * 1、Spark可以通过HiveContext直接操作Hive中的数据,基于HiveContext我们可以使用sql/hql两种方式编写SQL语句对Hive
 * 进行操作:创建、删除表,往表中导入数据,以及CRUD
 * 2、通过saveAsTable方式把DataFrame中的数据保存到Hive数据仓库中
 * 3、通过 HiveContext.table方式直接加载Hive中的表而生成DataFrame
*/
object SparkSQLWindowFunctionOps {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("SparkSQLWindowFunctionOps")
    val sc = new SparkContext(conf)

    val hiveContext = new HiveContext(sc)
    hiveContext.sql("use hive")
    hiveContext.sql("DROP TABLE IF EXISTS scores")
    hiveContext.sql("CREATE TABLE  IF NOT EXISTS scores(name STRING,score INT) ROW FORMAT DELIMITED FIELDS TERMINATED BY ' ' LINES TERMINATED BY '\\n'")

    //把要处理的数据导入到Hive表中
    hiveContext.sql("LOAD DATA LOCAL INPATH '/home/hadoop/resource/toNGroup.txt' INTO TABLE scores" )

    /*
     * 使用自查询完成目标数据的提取,在目标数据内使用窗口函数row_number来进行分组排序:
     * PARTITION BY :指定窗口函数分组的Key
     * ORDER BY :分组后进行排序
     */
    val result =  hiveContext.sql("SELECT name,score "
        + "FROM ("
            + "SELECT "
               + "name,"
               + "score, "
               + "row_number() OVER (PARTITION BY name ORDER BY score DESC ) rank"
               + " FROM scores"
        + ")  sub_scores "
        + " WHERE rank <= 4")

     result.show(); //在Driver的控制台上打印出结果

    hiveContext.sql("DROP TABLE IF EXISTS sortedResultScores")
    result.saveAsTable("sortedResultScores")
  }
}

执行结果

16/06/01 23:22:19 INFO DAGScheduler: ResultStage 3 (show at SparkSQLWindowFunctionOps.scala:46) finished in 6.969 s
16/06/01 23:22:19 INFO DAGScheduler: Job 1 finished: show at SparkSQLWindowFunctionOps.scala:46, took 7.284524 s
+------+-----+
|  name|score|
+------+-----+
| spark|   92|
| spark|   90|
| spark|   84|
| spark|   29|
|hadoop|   88|
|hadoop|   87|
|hadoop|   39|
|hadoop|   29|
+------+-----+

16/06/01 23:22:19 INFO ParseDriver: Parsing command: DROP TABLE IF EXISTS sortedResultScores
16/06/01 23:22:19 INFO ParseDriver: Parse Completed

总结

1、为什么称作 Window function呢?

"SELECT name,score "
        + "FROM ("
            + "SELECT "
               + "name,"
               + "score, "
               + "row_number() OVER (PARTITION BY name ORDER BY score DESC ) rank"
               + " FROM scores"
        + ")  sub_scores "
        + " WHERE rank <= 4"

row_number函数作用于一个分区(本例中就是 spark与hadoop形成的两个分区),并为该分区中的每条记录生成一个序列号,这样在外层循环就可以通过过滤该序列号(eg、rank<4)而取特定的数据。从功能上来看,row_number为外层查询操作里面的记录,打开了一扇窗户(写不下去了,这个说法实在有点勉强 ~~~),暂时就这么理解吧!

2、spark sql 通过hiveContext直接操作 hive仓库中的数据 - 这点实在太棒了啊啊啊!

你可能感兴趣的:(Spark)