hive grouping set 的 groupping_id() 的解析方法

场景

在编写报表的 sql 脚本的时候,可能会遇到多维度组合的情况,例如下面的情况。

- 省 城市 区 人口数量
	- 省 城市 区 街道 人口数量

常规做法

上面据的例子中,既要汇总出省、城市、区的人口,也要汇总出省、城市、区、街道的人口。常规的做法是编写两种维度组合的 sql ,然后再使用 union all 进行全集。
类似下面的写法:

 select  province
       , city
       , region
       , '-' as street
       , sum(population_quantity)
    from population
group by province
       , city
       , region
union all 
 select  province
       , city
       , region
       , street
       , sum(population_quantity)
    from population
group by province
       , city
       , region
       , street

grouping set 的解决方法

 select  province
       , city
       , region
       , street
       , sum(population_quantity)
    from population
group by province
       , city
       , region
       , street
grouping sets((province,city,region),(province,city,region,street))       

呵呵,我坦白,我写的不一定对,你要在自己的机子上试试,否则错,就浪费时间了。

那么问题了来,我怎么知道哪些行是统计到省、城市、区,哪些行是统计到省、城市、区、街道的。这时候就要请我们的 Grouping__ID 闪亮登场了。那么我们的 sql 也要改成如下的样子:

 select  province
       , city
       , region
       , street
       , Grouping__ID as group_id
       , sum(population_quantity)
    from population
group by province
       , city
       , region
       , street
grouping sets((province,city,region),(province,city,region,street))       

Grouping__ID 是 hive 帮我们生成的,标识维度组合的十进制数字。那么它的生成规则是怎样的呢?

以上面的例子说明 Grouping__ID 的生成规则:
group by 后面有四个字段,在 grouping sets 的组合中存在的字段使用 1 表示,否则使用 0 表示。
grouping sets 中有两组字段组合,分别是:
province,city,region:1 1 1 0 换算成十进制为 14
province,city,region,street:1 1 1 1 换算成十进制为 15
Grouping__ID 就是最后生成的十进制数字。

发现了这个规律后,我们应该更进一步,利用算法,让 sql 帮我计算出十进制代表了那些维度组合。

我们知道二进制的并运算,例如,1 0 0 0 & x ,其中 x 是任意一个四位二进制数字,经过并运算,我们得到一个 y ,如何 y = 8 ,那说明 x 的第四数是 1 ,如果 y 是 0 ,那说明 x 的第四位数是 0。根据这一原理,我们就能判断出 Grouping__ID 代表的维度组合中有那些字段了。

例如,我们得到一个结果表,population_sum。

select concat(   if(group_id & (1<<0) = (1<<0) ) , 'province', '')
               ,'-', if(group_id & (1<<1) = (1<<1) ) , 'city', '')
               ,'-', if(group_id & (1<<2) = (1<<2) ) , 'region', '')
               ,'-', if(group_id & (1<<3) = (1<<3) ) , 'street', '')               
             )  as dim_group -- 维度组合 
  from population_sum

上面的 sql 语句是 mysql 上运行的,如果你所在的平台(例如,presto)没有&和为运算,那改怎么办呢?这里介绍一下,我想出来的一个办法,在每一个符合 sql 标准的数据平台中,应该都会支持指数运算和取余运算,例如,8 是的二进制是 1000, 8/2=4,二
进制为 100,8/8=1,二进制是1。通过上面的例子,可以看出,除 2 相当于向右进行位运算,如果一个二进制数对应的十进制数除以 2 的 n - 1 次方,则可以将这个二进制的数的第 n 位放到对应 10 进制数的各位,然后进行取余运算,如果结果是 0 ,则第 N 位是 0 ,否则为 1 。

select mod(8/pow(2,3),2) -> 1 表示第四位上的是 1 

大概就是这样吧。我也不知道我写的到底对不对,各位看官,看懂了,还要自己测试一下。

猜你喜欢

转载自blog.csdn.net/bluedraam_pp/article/details/80669485
今日推荐