今天学习 Spark SQL 的窗口函数,终于解决了之前用普通聚合函数搞不定的分组内排名、累计统计这类复杂需求,这也是实际业务中最常用的功能,学完感觉结构化数据处理的能力又上了一个台阶。之前用groupBy做聚合,结果会按分组字段合并行,没法保留原数据的详细信息,比如想统计每个城市薪资前 3 的用户,还得保留用户姓名、年龄,普通聚合根本做不到,窗口函数刚好能解决这个问题 ——分组不合并行,在原数据基础上增加统计列,这是核心优势。
先把窗口函数的基础语法摸透,核心就三步:用row_number()/rank()/dense_rank()定义统计规则,用partitionBy()指定分组字段,用orderBy()指定排序规则,再通过over()把这些规则包裹起来。试了最常用的三个排名函数,终于分清了区别:row_number()是纯连续排名,同分组同分数也会排 1、2、3;rank()会跳级,同分数同排名,后续排名跳空;dense_rank()不跳级,同分数同排名,后续排名连续,用薪资排名试了一遍,效果一眼就能区分开,记起来也不费劲。
接着用实际的用户数据练手,把常用的窗口函数全过了一遍:用row_number() over(partitionBy("城市") orderBy("薪资") desc)做每个城市薪资 TOP3 筛选,用sum("薪资") over(partitionBy("城市") orderBy("入职时间") rows between unbounded preceding and current row)做每个城市的薪资累计统计,还用avg("薪资") over(partitionBy("城市"))给每个用户增加所属城市的平均薪资列,全程用 Spark SQL 写,语法和 Hive SQL 几乎一致,写起来特别顺手。