前言
SQL 中 Group By
语句大家都很熟悉, 根据指定的规则对数据进行分组 ,常常和 聚合函数 一起使用。
比如,考虑有表 dealer
,表中数据如下:
如果执行 SQL 语句 SELECT id, sum(quantity) FROM dealer GROUP BY id ORDER BY id
,会得到如下结果:
+---+-------------+ | id|sum(quantity)| +---+-------------+ |100| 32| |200| 33| |300| 13| +---+-------------+
上述 SQL 语句的意思就是对数据按 id
列进行分组,然后在每个分组内对 quantity
列进行求和。
Group By
语句除了上面的简单用法之外,还有更高级的用法,常见的是 Grouping Sets
、 RollUp
和 Cube
,它们在 OLAP 时比较常用。其中, RollUp
和 Cube
都是以 Grouping Sets
为基础实现的,因此,弄懂了 ,也就理解了 和 。
本文首先简单介绍 Grouping Sets
的用法,然后以 Spark SQL 作为切入点,深入解析 Grouping Sets
的实现机制。
Spark SQL 是 Apache Spark 大数据处理框架的一个子模块,用来处理结构化信息。它可以将 SQL 语句翻译多个任务在 Spark 集群上执行, 允许用户直接通过 SQL 来处理数据 ,大大提升了易用性。
Grouping Sets 简介
Spark SQL 官方文档中 一节对 Grouping Sets
语句的描述如下:
Groups the rows for each grouping set specified after GROUPING SETS. (... 一些举例) This clause is a shorthand for a UNION ALL
where each leg of the UNION ALL
operator performs aggregation of each grouping set specified in the GROUPING SETS
clause. (... 一些举例)
也即, Grouping Sets
语句的作用是指定几个 grouping set 作为 Group By
的分组规则,然后再将结果联合在一起。它的效果和, 先分别对这些 grouping set 进行 Group By
分组之后,再通过 Union All 将结果联合起来 ,是一样的。
比如,对于 dealer
表, Group By Grouping Sets ((city, car_model), (city), (car_model), ())
和 Union All((Group By city, car_model), (Group By city), (Group By car_model), 全局聚合)
的效果是相同的:
先看 Grouping Sets 版的执行结果:
spark-sql> SELECT city, car_model, sum(quantity) AS sum FROM dealer > GROUP BY GROUPING SETS ((city, car_model), (city), (car_model), ()) > ORDER BY city, car_model; +--------+------------+---+ | city| car_model|sum| +--------+------------+---+ | null| null| 78| | null|Honda Accord| 33| | null| Honda CRV| 10| | null| Honda Civic| 35| | Dublin| null| 33| | Dublin|Honda Accord| 10| | Dublin| Honda CRV| 3| | Dublin| Honda Civic| 20| | Fremont| null| 32| | Fremont|Honda Accord| 15| | Fremont| Honda CRV| 7| | Fremont| Honda Civic| 10| |San Jose| null| 13| |San Jose|Honda Accord| 8| |San Jose| Honda Civic| 5| +--------+------------+---+
再看 Union All 版的执行结果:
spark-sql> (SELECT city, car_model, sum(quantity) AS sum FROM dealer GROUP BY city, car_model) UNION ALL > (SELECT city, NULL as car_model, sum(quantity) AS sum FROM dealer GROUP BY city) UNION ALL > (SELECT NULL as city, car_model, sum(quantity) AS sum FROM dealer GROUP BY car_model) UNION ALL > (SELECT NULL as city, NULL as car_model, sum(quantity) AS sum FROM dealer) > ORDER BY city, car_model; +--------+------------+---+ | city| car_model|sum| +--------+------------+---+ | null| null| 78| | null|Honda Accord| 33| | null| Honda CRV| 10| | null| Honda Civic| 35| | Dublin| null| 33| | Dublin|Honda Accord| 10| | Dublin| Honda CRV| 3| | Dublin| Honda Civic| 20| | Fremont| null| 32| | Fremont|Honda Accord| 15| | Fremont| Honda CRV| 7| | Fremont| Honda Civic| 10| |San Jose| null| 13| |San Jose|Honda Accord| 8| |San Jose| Honda Civic| 5| +--------+------------+---+
两版的查询结果完全一样。
Grouping Sets 的执行计划
从执行结果上看,Grouping Sets 版本和 Union All 版本的 SQL 是等价的,但 Grouping Sets 版本更加简洁。
那么, Grouping Sets
仅仅只是 Union All
的一个缩写,或者语法糖吗 ?
为了进一步探究 Grouping Sets
的底层实现是否和 Union All
是一致的,我们可以来看下两者的执行计划。
首先,我们通过 explain extended
来查看 Union All 版本的 Optimized Logical Plan :
spark-sql> explain extended (SELECT city, car_model, sum(quantity) AS sum FROM dealer GROUP BY city, car_model) UNION ALL(SELECT city, NULL as car_model, sum(quantity) AS sum FROM dealer GROUP BY city) UNION ALL (SELECT NULL as cit