参考
场景
将本地文件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仓库中的数据 - 这点实在太棒了啊啊啊!