说到分组,我们很快就想到group by,但是如果在分组的基础上进行取TopN,我们很快又想到开窗函数,group by一般和聚合函数搭配使用,那么聚合函数和开窗函数有啥区别呢?
普通的聚合函数聚合的行集是组,开窗函数聚合的行集是窗口。因此,普通的聚合函数每组(Group by)只返回一个值,而开窗函数则可为窗口中的每行都返回一个值。简单理解,就是对查询的结果多出一列,这一列可以是聚合值,也可以是排序值。
开窗函数一般分为两类,聚合开窗函数和排序开窗函数。
接下来通过几个简单的案例来看使用Spark如何求分组取TopN的需求
//company app visit_times
腾讯,腾讯视频,800
腾讯,QQ音乐,900
腾讯,微信读书,100
腾讯,微信,900
腾讯,腾讯课堂,200
阿里,支付宝,900
阿里,优酷视频,700
阿里,虾米音乐,500
阿里,飞猪,700
阿里,钉钉,600
百度,百度App,700
百度,百度地图,800
百度,爱奇艺,800
百度,百度钱包,100
百度,百度贴吧,200
val spark = SparkSession
.builder()
.appName(this.getClass.getSimpleName)
.master(master = "local[*]")
.getOrCreate()
import spark.implicits._
val df = spark.read.textFile("./data/app")
.map(_.split(","))
.map(x => (x(0), x(1), x(2)))
.toDF("company", "app", "vst_times")
.cache()
df.createTempView(viewName = "view1")
import spark.sql
sql(sqlText = "select company,app,vst_times from " +
"(select company,app,vst_times,row_number() " +
"over(partition by company order by vst_times desc) " +
"as rn from view1) as t where t.rn <= 2")
.show()
这里使用的是Hive的row_number()函数,语法格式:
row_number() over (partition by COL1 order by COL2 desc ) rank
结果:
+-------+--------+---------+
|company| app|vst_times|
+-------+--------+---------+
| 腾讯| QQ音乐| 900|
| 腾讯| 微信| 900|
| 百度|百度地图| 800|
| 百度| 爱奇艺| 800|
| 阿里| 支付宝| 900|
| 阿里|优酷视频| 700|
+-------+--------+---------+
腾讯,微信,500
腾讯,微信,800
腾讯,微信,1000
腾讯,微信,600
腾讯,腾讯视频,800
腾讯,腾讯视频,300
腾讯,腾讯视频,700
腾讯,腾讯视频,700
腾讯,QQ音乐,900
腾讯,QQ音乐,500
腾讯,QQ音乐,900
腾讯,QQ音乐,600
腾讯,微信读书,100
腾讯,微信读书,200
腾讯,微信读书,300
腾讯,微信读书,100
腾讯,腾讯课堂,100
腾讯,腾讯课堂,200
腾讯,腾讯课堂,300
腾讯,腾讯课堂,100
import org.apache.spark.sql.functions._
df.select("company", "app", "vst_times")
.groupBy("company", "app")
.agg(sum($"vst_times").alias("s_vst"))
.sort($"s_vst".desc)
.limit(3)
.show()
结果:
+-------+--------+------+
|company| app| s_vst|
+-------+--------+------+
| 腾讯| QQ音乐|2900.0|
| 腾讯| 微信|2900.0|
| 腾讯|腾讯视频|2500.0|
+-------+--------+------+