grouping sets

如果说聚合函数(Simple UDAF / Generic UDAF)是HQL聚合数据查询或分析的中枢处理器,那GROUP BY可以说是聚合函数的神经了,GROUP BY收集和传递材料,然后交给聚合函数们去处理。这些材料的组织形式显得尤为重要,它们表达着分析者想要的观察维度或视角,管理着聚合函数们的操作对象。

       而分析者经常想要在一次分析中从多个维度去获得分析数据,对包含多个维度或多级层次的分析,上卷(roll up)或下钻(drill down)一类就很有分析价值。

       我们有时候可以从最细、最多的粒度去做一个查询,然后把结果集导入Excel这个数据分析利器,用数据透视图标进行“上卷”分析;但有时候也行不通,比如说UV这种需要去重的数据,在Excel里用汇总方式进行上卷,就不是纯粹的UV概念了。

       所以,对这种情形,在查询过程中,我们就需要获得已经下钻和上卷的数据;如果只有GROUP BY子句,那我们可以写出按各个维度或层次进行GROUP BY的查询语句,然后再通过UNION子句把结果集拼凑起来,但是这样的查询语句显得冗长、笨拙。

       为此,HQL像其它很多SQL实现一样,为我们提供了GROUPINGSETS子句来简化查询语句的编写,以下官方CWiki文档很清晰地表达了GROUPING SETS的功能:
 

Aggregate Query with GROUPING SETS Equivalent Aggregate Query with GROUP BY
SELECT a, b, SUM(c) FROM tab1 GROUP BY a, b GROUPING SETS ( (a,b) ) SELECT a, b, SUM(c) FROM tab1 GROUP BY a, b
SELECT a, b, SUM( c ) FROM tab1 GROUP BY a, b GROUPING SETS ( (a,b), a) SELECT a, b, SUM( c ) FROM tab1 GROUP BY a, b
UNION
SELECT a, null, SUM( c ) FROM tab1 GROUP BY a
SELECT a,b, SUM( c ) FROM tab1 GROUP BY a, b GROUPING SETS (a,b) SELECT a, null, SUM( c ) FROM tab1 GROUP BY a
UNION
SELECT null, b, SUM( c ) FROM tab1 GROUP BY b
SELECT a, b, SUM( c ) FROM tab1 GROUP BY a, b GROUPING SETS ( (a, b), a, b, ( ) ) SELECT a, b, SUM( c ) FROM tab1 GROUP BY a, b
UNION
SELECT a, null, SUM( c ) FROM tab1 GROUP BY a, null
UNION
SELECT null, b, SUM( c ) FROM tab1 GROUP BY null, b
UNION
SELECT null, null, SUM( c ) FROM tab1

因为涉及UNION操作,所以为了遵循UNION对参与合并的数据集合的要求,GROUPING SETS会把在单个GROUP BY逻辑中没有参与GROUP BY的那一列置为NULL值,使它成为常量占位列。这样聚合出来的结果,未被GROUP BY的列将显示为NULL。

       但是这样的处理也会引起一个歧义性问题,如果我们分析的表有一些列没有NOT NULL约束,那原始数据中,未被GROUP BY的列可能原本就会出现一些NULL值,这样,GROUPING SETS出来的结果,我们没有办法去区分该列显示的NULL值是原始数据出现的NULL值聚合的结果,还是你因为这列没有参与GROUP BY而被置为NULL值的结果。

       为了解决这个歧义问题,HQL又为我们提供了一个Grouping__ID函数(请注意函数名中的下划线是两个!);这个函数没有参数,在有GROUPING SETS子句的情况下,把它直接放在SELECT子句中,像其它列一样,独占一列。它返回的结果是一个看起来像整形数值类型,其实是字符串的值,这个值使用了位图策略(bitvector,位向量),即它的二进制形式中的每1位标示着对应列是否参与GROUP BY,如果某一列参与了GROUP BY,对应位就被置为1,否则为0,根据这个位向量值和对应列是否显示为NULL,我们就可以解决上面提到的歧义问题了。

       这样一来,Grouping__ID函数返回值的范围由查询的字段数(除去聚合函数产生的列)决定,如果比如有3列,那位向量为3位,最大值为7。CWiki文档提供了下面的示例:

       有下面一个表数据:

我们用这样的查询语句去执行查询:

SELECT key, value, GROUPING__ID, count(*) from T1 GROUP BY key,value WITH ROLLUP

得到如下结果:

官方文档没有明确说明这个位向量和各列的高低位对应关系,但是从示例我们可以看到,这个位向量的低位对应SELECT子句中的第1列(非聚合列),高位对应最后1列(非聚合列)。

       上面的查询用到了WITH ROLLUP子句,它对应SQL中的上卷操作,其实它就是GROUPINGSETS的特例,对应上面第一个表格中的第4种情形;根据官方的CWiki文档解释,GROUP BY 子句加上ROLLUP 子句可用于计算从一个维度进行层级聚合的操作:

GROUP BY a, b, c with ROLLUP assumes that the hierarchy is"a" drilling down to "b" drilling down to "c".


       类似地还有WITH CUBE子句,对应SQL中的CUBE操作,它完成对字段列中的所有可能组合(全序集?)进行GROUP BY的功能,正如官方CWiki文档的解释:

GROUP BY a, b, c WITH CUBE 等同于
GROUP BY a, b, c GROUPING SETS ( (a, b, c), (a, b), (b, c), (a, c),(a), (b), (c), ( ))

 
       GROUPING SETS增强了GROUP BY的查询表达能力,ROLLUP和CUBE又增强了GROUPING SETS的查询表达能力,这样一来,GROUP BY的形态也变得多样化了,让我们能够在查询阶段就实现更多的分析角度。

       还需留意的是:Hive从0.10.0版本才开始有GROUPING SETS的。

官方CWiki文档请点我:https://cwiki.apache.org/confluence/display/Hive/Enhanced+Aggregation,+Cube,+Grouping+and+Rollup

猜你喜欢

转载自blog.csdn.net/twfx1027/article/details/89881967