背景:MOLAP、ROLAP、HOLAP的区别与联系
在线分析处理(OLAP,Online Analytical Processing)是通过带层次的维度和跨维度进行多维分析的,简单理解为一种多维数据分析的方式,通过OLAP可以展示数据仓库中数据的多维逻辑视图。在多维分析中,数据是按照维度(观察数据的角度)来表示的,比如商品、城市、客户。而维通常按层次(层次维度)组织的,如城市、省、国家,再比如时间也是有层次的,如天、周、月、季度和年。不同的管理者可以从不同的维度(视角)去观察这些数据,这些在多个不同维度上对数据进行综合考察的手段就是通常所说的数据仓库多维查询,最常见的就如上卷(roll-up)和下钻(drill-down)了,所谓上卷,指的是选定特定的数据范围之后,对其进行汇总统计以获取更高层次的信息。所谓下钻,指的是选定特定的数据范围之后,需要进一步查看细节的数据。从另一种意义上说,钻取就是针对多维展现的数据,进一步探究其内部的组成和来源。值得注意的是,上卷和下钻要求维度具有层级结构,即数仓中所说的层次维度。
Hive提供了多维数据分析的函数,如GROUPING SETS,GROUPING_ID,CUBE,ROLLUP,通过这些分析函数,可以轻而易举的实现多维数据分析。下面将会通过一个案例来了解这些函数的具体含义以及该如何使用这些函数。注意:在hive中使用这些函数之前,要确保开启了map端聚合,即set hive.map.aggr=true,否则会报如下错误:
示例模板
-- 使用GROUPING SETS查询
SELECT a,
b,
SUM(c)
FROM tab1
GROUP BY a,b
GROUPING SETS ((a,b), a,b, ());
-- 与GROUP BY等价关系
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;
当使用聚合时,有时候会出现数据本身为null值,很难区分究竟是数据列本身为null值还是聚合数据行为null,即无法区分查询结果中的null值是属于列本身的还是聚合的结果行,因此需要一种方法识别出列中的null值。grouping__id 函数就是此场景下的解决方案。注意该函数是有两个下划线。这个函数为每种聚合数据行生成唯一的组id。它的返回值看起来像整型数值,其实是字符串类型,这个值使用了位图策略(bitvector,位向量),即它的二进制形式中的每一位表示对应列是否参与分组,如果某一列参与了分组,对应位就被置为1,否则为0。通过这种方式可以区分出数据本身中的null值。看到这是不是还是一头雾水,没关系,来看下面的示例:
-- 创建表
create table test_grouping__id(id int,amount decimal(10,2));
-- 插入数据
insert into table test_grouping__id values(1,null),(1,1),(2,2),(3,3),(3,null),(4,5);
--执行查询
SELECT id,
amount,
grouping__id,
count(*) cnt
FROM test_grouping__id
GROUP BY id,
amount
GROUPING sets(id,(id,amount),())
ORDER BY grouping__id
查询结果分析
查询结果如下图所示:绿色框表示未进行分组,即进行全局聚合,grouping_id等于0,表示没有字段参与分组。蓝色框表示按照id进行分组,对应的grouping_id为1,表示只有一个字段参与了分组。橘色的框表示按照id和amount两个字段进行分组,grouping_id为3,即有两个字段参与了分组,转成十进制为2^0 + 2^1 = 3。
以上面为例,分组字段为id、amount,转成二进制表示形式为:
最左侧的分组字段必须参与分组
,比如a字段是最左侧的字段,则a必定参与分组组合。示例模板
-- 使用WITH ROLLUP查询
SELECT a,
b,
c
SUM(d)
FROM tab1
GROUP BY a,b,c
WITH ROLLUP
-- 等价于下面的方式
SELECT a,
b,
c,
SUM(d)
FROM tab1
GROUP BY a,b,c
GROUPING SETS ((a,b,c), (a,b), (a),());
其实,可以将上面的情况抽象成排列组合的问题,即从分组字段集合(假设有n个字段)中随意取出0~n个字段,那么会有多少中组合方式,如下面公式所示:
结合上面的例子,GROUP BY a,b,c WITH CUBE,那么所有的组合方式有:(a,b,c),(a,b),(b,c), (a,c),(a),(b),(c),(),一共有8种组合,即2^3 = 8。
-- 使用WITH CUBE查询
SELECT a,
b,
c
SUM(d)
FROM tab1
GROUP BY a,b,c
WITH CUBE
-- 等价于下面的方式
SELECT a,
b,
c,
SUM(d)
FROM tab1
GROUP BY a,b,c
GROUPING SETS ((a,b,c),(a,b),(b,c), (a,c),(a),(b),(c),());
SELECT t.type,
t.status,
count(1),
grouping__id
FROM ods_callcenter.sdm_call t
WHERE dt = '20200601'
GROUP BY t.type,
t.status
WITH CUBE
--WITH ROLLUP
--GROUPING SETS(t.status,t.type,(t.status,t.type))