一、层次维度简介

大多数维度都具有一个或多个层次。例如,示例数据仓库中的日期维度就有一个四级层次:年、季度、月和日。这些级别用date_dim表里的列表示。日期维度是一个单路径层次,因为除了年-季度-月-日这条路径外,它没有任何其它层次。为了识别数据仓库里一个维度的层次,首先要理解维度中列的含义,然后识别两个或多个列是否具有相同的主题。例如,年、季度、月和日具有相同的主题,因为它们都是关于日期的。具有相同主题的列形成一个组,组中的一列必须包含至少一个组内的其它成员(除了最低级别的列),如在前面提到的组中,月包含日。这些列的链条形成了一个层次,例如,年-季度-月-日这个链条是一个日期维度的层次。除了日期维度,邮编维度中的地理位置信息,产品维度的产品与产品分类,也都构成层次关系。表1显示了三个维度的层次。

zip_code_dim

product_dim

date_dim

zip_code

product_name

date

city

product_category

month

state

quarter

year

表1

本篇描述处理层次关系的方法,包括在固定深度的层次上进行分组和钻取查询,多路径层次和参差不齐层次的处理等,从最基本的情况开始讨论。

二、固定深度的层次

固定深度层次是一种一对多关系,例如,一年中有四个季度,一个季度包含三个月等等。当固定深度层次定义完成后,层次就具有固定的名称,层次级别作为维度表中的不同属性出现。只要满足上述条件,固定深度层次就是最容易理解和查询的层次关系,固定层次也能够提供可预测的、快速的查询性能。可以在固定深度层次上进行分组和钻取查询。
        分组查询是把度量按照一个维度的一个或多个级别进行分组聚合。下面的脚本是一个分组查询的例子。该查询按产品(product_category列)和日期维度的三个层次级别(year、quarter和month列)分组返回销售金额。

  1. select product_category,year,quarter,month,sum(order_amount) s_amount
  2. from v_sales_order_fact a,product_dim b,date_dim c
  3. where a.product_sk = b.product_sk
  4. and a.year_month = c.year * 100 + c.month
  5. group by product_category, year, quarter, month
  6. order by product_category, year, quarter, month;

这是一个非常简单的分组查询,结果输出的每一行度量(销售订单金额)都沿着年-季度-月的层次分组。
        与分组查询类似,钻取查询也把度量按照一个维度的一个或多个级别进行分组。但与分组查询不同的是,分组查询只显示分组后最低级别、即本例中月级别上的度量,而钻取查询显示分组后维度每一个级别的度量。下面使用两种方法进行钻取查询,结果显示了每个日期维度级别,即年、季度和月各级别的订单汇总金额。

  1. -- 使用union all
  2. select product_category, time, order_amount
  3. from (select product_category,
  4. case when sequence = 1 then 'year: '||time
  5. when sequence = 2 then 'quarter: '||time
  6. else 'month: '||time
  7. end time,
  8. order_amount,
  9. sequence,
  10. date
  11. from (select product_category, min(date) date, year time, 1 sequence, sum(order_amount) order_amount
  12. from v_sales_order_fact a, product_dim b, date_dim c
  13. where a.product_sk = b.product_sk
  14. and a.year_month = c.year * 100 + c.month
  15. group by product_category , year
  16. union all
  17. select product_category, min(date) date, quarter time, 2 sequence, sum(order_amount) order_amount
  18. from v_sales_order_fact a, product_dim b, date_dim c
  19. where a.product_sk = b.product_sk
  20. and a.year_month = c.year * 100 + c.month
  21. group by product_category , year , quarter
  22. union all
  23. select product_category, min(date) date, month time, 3 sequence, sum(order_amount) order_amount
  24. from v_sales_order_fact a, product_dim b, date_dim c
  25. where a.product_sk = b.product_sk
  26. and a.year_month = c.year * 100 + c.month
  27. group by product_category , year , quarter , month) x) y
  28. order by product_category , date , sequence , time;
  29.  
  30. -- 使用grouping sets
  31. select product_category,
  32. case when gid = 3 then 'year: '||year
  33. when gid = 1 then 'quarter: '||quarter
  34. else 'month: '||month
  35. end time,
  36. order_amount
  37. from (select product_category, year, quarter, month, min(date) date, sum(order_amount) order_amount,grouping(product_category,year,quarter,month) gid
  38. from v_sales_order_fact a, product_dim b, date_dim c
  39. where a.product_sk = b.product_sk
  40. and a.year_month = c.year * 100 + c.month
  41. group by grouping sets ((product_category,year,quarter,month),(product_category,year,quarter),(product_category,year))) x
  42. order by product_category , date , gid desc, time;

以上两种不同写法的查询语句结果相同,如图1所示。

图1

第一条语句的子查询中使用union all集合操作将年、季度、月三个级别的汇总数据联合成一个结果集。注意union all的每个查询必须包含相同个数和类型的字段。附加的min(date)和sequence导出列用于对输出结果排序显示。这种写法使用标准的SQL语法,具有通用性。
        第二条语句使用HAWQ提供的grouping函数和group by grouping sets子句。Grouping set就是对列出的每一个字段组进行group by操作,如果字段组为空,则不进行分组处理。因此该语句会生成按产品类型、年、季度、月;类型、年、季度;类型、年分组的聚合数据行。
        grouping(<column> [, …])函数用于区分查询结果中的null值是属于列本身的还是聚合的结果行。该函数为每个参数产生一位0或1,1代表结果行是聚合行,0表示结果行是正常分组数据行。函数值使用了位图策略(bitvector,位向量),即它的二进制形式中的每一位表示对应列是否参与分组,如果某一列参与了分组,对应位就被置为1,否则为0。最后将二进制数转换为十进制数返回。通过这种方式可以区分出数据本身中的null值。

三、多路径层次

多路径层次是对单路径层次的扩展。现在数据仓库的月维度只有一条层次路径,即年-季度-月这条路径。现在增加一个新的“促销期”级别,并且加一个新的年-促销期-月的层次路径。这时月维度将有两条层次路径,因此是多路径层次维度。下面的脚本给month_dim表添加一个叫做campaign_session的新列,并建立rds.campaign_session过渡表。

  1. alter table tds.month_dim add column campaign_session varchar(30) default null;
  2. comment on column tds.month_dim.campaign_session is '促销期';
  3.  
  4. create table rds.campaign_session
  5. (campaign_session varchar(30),month smallint,year smallint);

假设所有促销期都不跨年,并且一个促销期可以包含一个或多个月份,但一个月份只能属于一个促销期。为了理解促销期如何工作,表2给出了一个促销期定义的示例。

月—12月

表2

每个促销期有一个或多个月。一个促销期也许并不是正好一个季度,也就是说,促销期级别不能上卷到季度,但是促销期可以上卷至年级别。假设2017年促销期的数据如下,并保存在/home/gpadmin/campaign_session.csv文件中。

  1. 2016 First Campaign,1,2017
  2. 2017 First Campaign,2,2017
  3. 2017 First Campaign,3,2017
  4. 2017 First Campaign,4,2017
  5. 2017 Second Campaign,5,2017
  6. 2017 Second Campaign,6,2017
  7. 2017 Second Campaign,7,2017
  8. 2017 Third Campaign,8,2017
  9. 2017 Last Campaign,9,2017
  10. 2017 Last Campaign,10,2017
  11. 2017 Last Campaign,11,2017
  12. 2017 Last Campaign,12,2017

现在可以执行下面的脚本把2017年的促销期数据装载进月维度。

  1. copy rds.campaign_session from '/home/gpadmin/campaign_session.csv' with delimiter ',';
  2.  
  3. set search_path = tds;
  4.  
  5. create table tmp as
  6. select t1.month_sk month_sk,
  7. t1.month month1,
  8. t1.month_name month_name,
  9. t1.quarter quarter,
  10. t1.year year1,
  11. t2.campaign_session campaign_session
  12. from month_dim t1
  13. left join rds.campaign_session t2 on t1.year = t2.year
  14. and t1.month = t2.month;
  15. truncate table month_dim;
  16. insert into month_dim select * from tmp;
  17. drop table tmp;

此时查询月份维度表,可以看到2017年的促销期已经有数据,其它年份的campaign_session字段值为null,如图2所示。

图2

注意:

  • 本地文件必须在HAWQ master主机上的本地目录中。
  • copy命令需要使用gpadmin用户执行。

四、参差不齐的层次

在一个或多个级别上没有数据的层次称为不完全层次。例如在特定月份没有促销期,那么月维度就具有不完全促销期层次。下面是一个不完全促销期的例子,数据存储在ragged_campaign.csv文件中。2017年1月、4月、6月、9月、10月、11月和12月没有促销期。

  1. ,1,2017
  2. 2017 Early Spring Campaign,2,2017
  3. 2017 Early Spring Campaign,3,2017
  4. ,4,2017
  5. 2017 Spring Campaign,5,2017
  6. ,6,2017
  7. 2017 Last Campaign,7,2017
  8. 2017 Last Campaign,8,2017
  9. ,9,2017
  10. ,10,2017
  11. ,11,2017
  12. ,12,2017

重新向month_dim表装载促销期数据。

  1. truncate table rds.campaign_session;
  2. copy rds.campaign_session from '/home/gpadmin/ragged_campaign.csv' with delimiter ',';
  3.  
  4. set search_path = tds;
  5.  
  6. create table tmp as
  7. select t1.month_sk month_sk,
  8. t1.month month1,
  9. t1.month_name month_name,
  10. t1.quarter quarter,
  11. t1.year year1,
  12. null campaign_session
  13. from month_dim t1;
  14.  
  15. truncate table month_dim;
  16.  
  17. insert into month_dim
  18. select t1.month_sk,
  19. t1.month1,
  20. t1.month_name,
  21. t1.quarter,
  22. t1.year1,
  23. case when t2.campaign_session != '' then t2.campaign_session
  24. else t1.month_name
  25. end campaign_session
  26. from tmp t1 left join rds.campaign_session t2
  27. on t1.year1 = t2.year and t1.month1 = t2.month;
  28.  
  29. drop table tmp;

在有促销期的月份,campaign_session列填写促销期名称,而对于没有促销期的月份,该列填写月份名称。轻微参差不齐层次没有固定的层次深度,但层次深度有限。如地理层次深度通常包含3到6层。与其使用复杂的机制构建难以预测的可变深度层次,不如将其变换为固定深度位置设计,针对不同的维度属性确立最大深度,然后基于业务规则放置属性值。

下面的语句查询年-促销期-月层次,查询结果如图3所示。

  1. select product_category,       
  2. case when gid = 3 then cast(year as varchar(10))      
  3.         when gid = 1 then campaign_session     
  4.         else month_name
  5.      end time,   
  6.      order_amount    
  7.   from (select product_category, 
  8. year, 
  9. campaign_session, 
  10. month, 
  11. month_name, 
  12. sum(month_order_amount) order_amount, 
  13. sum(month_order_quantity) order_quantity,
  14. grouping(product_category,year,campaign_session,month) gid,
  15. min(month) min_month   
  16.        from v_month_end_sales_order_fact a, product_dim b, month_dim c        
  17. where a.product_sk = b.product_sk     
  18. and a.year_month = c.year * 100 + c.month  
  19. and c.year = 2017                
  20. group by grouping sets ((product_category,year,campaign_session,month,month_name),(product_category,year,campaign_session),(product_category,year))) x    
  21. order by product_category, min_month, gid desc, month;   

图3

        min_month列用于排序。在有促销期月份的路径,月级别行的汇总与促销期级别的行相同。而对于没有促销期的月份,其促销期级别的行与月级别的行相同。也就是说,在没有促销期级别的月份,月上卷了它们自己。例如,2017年6月没有促销期,所以在输出看到,每种产品分类有两个相同的6月的行,其中后一行是月份级别的行,前一行表示是没有促销期的行。对于没有促销期的月份,促销期行的销售订单金额(输出里的order_amount列)与月分行的相同。

五、递归

HAWQ不支持递归查询,但支持递归函数,具体可参考“HAWQ技术解析(十) —— 过程语言”中的“UDF实例——递归树形遍历”。

HAWQ取代传统数仓实践(十八)——层次维度的更多相关文章

  1. HAWQ取代传统数仓实践(八)——维度表技术之角色扮演维度

    单个物理维度可以被事实表多次引用,每个引用连接逻辑上存在差异的角色维度.例如,事实表可以有多个日期,每个日期通过外键引用不同的日期维度,原则上每个外键表示不同的日期维度视图,这样引用具有不同的含义.这 ...

  2. HAWQ取代传统数仓实践(九)——维度表技术之退化维度

    退化维度技术减少维度的数量,简化维度数据仓库模式.简单的模式比复杂的更容易理解,也有更好的查询性能.        有时,维度表中除了业务主键外没有其它内容.例如,在本销售订单示例中,订单维度表除了订 ...

  3. HAWQ取代传统数仓实践(七)——维度表技术之维度子集

    有些需求不需要最细节的数据.例如更想要某个月的销售汇总,而不是某天的数据.再比如相对于全部的销售数据,可能对某些特定状态的数据更感兴趣等.此时事实数据需要关联到特定的维度,这些特定维度包含在从细节维度 ...

  4. HAWQ取代传统数仓实践(十一)——维度表技术之维度合并

    有一种合并维度的情况,就是本来属性相同的维度,因为某种原因被设计成重复的维度属性.例如,在销售订单示例中,随着数据仓库中维度的增加,我们会发现有些通用的数据存在于多个维度中.客户维度的客户地址相关信息 ...

  5. HAWQ取代传统数仓实践(十九)——OLAP

    一.OLAP简介 1. 概念 OLAP是英文是On-Line Analytical Processing的缩写,意为联机分析处理.此概念最早由关系数据库之父E.F.Codd于1993年提出.OLAP允 ...

  6. HAWQ取代传统数仓实践(十六)——事实表技术之迟到的事实

    一.迟到的事实简介 数据仓库通常建立于一种理想的假设情况下,这就是数据仓库的度量(事实记录)与度量的环境(维度记录)同时出现在数据仓库中.当同时拥有事实记录和正确的当前维度行时,就能够从容地首先维护维 ...

  7. HAWQ取代传统数仓实践(十三)——事实表技术之周期快照

    一.周期快照简介 周期快照事实表中的每行汇总了发生在某一标准周期,如一天.一周或一月的多个度量.其粒度是周期性的时间段,而不是单个事务.周期快照事实表通常包含许多数据的总计,因为任何与事实表时间范围一 ...

  8. HAWQ取代传统数仓实践(十四)——事实表技术之累积快照

    一.累积快照简介 累积快照事实表用于定义业务过程开始.结束以及期间的可区分的里程碑事件.通常在此类事实表中针对过程中的关键步骤都包含日期外键,并包含每个步骤的度量,这些度量的产生一般都会滞后于数据行的 ...

  9. HAWQ取代传统数仓实践(十)——维度表技术之杂项维度

    一.什么是杂项维度 简单地说,杂项维度就是一种包含的数据具有很少可能值的维度.事务型商业过程通常产生一系列混杂的.低基数的标志位或状态信息.与其为每个标志或属性定义不同的维度,不如建立单独的将不同维度 ...

随机推荐

  1. 60. Permutation Sequence(求全排列的第k个排列)

    The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the p ...

  2. Mysql5.7 用户与授权

    mysql -uroot -proot MySQL5.7 mysql.user表没有password字段改 authentication_string: 一. 创建用户: 命令:CREATE USER ...

  3. CAS单点登录的配置

    先说单点登录是个啥? 单点登录主要用于多系统集成,即在多个系统中,用户只需要到一个中央服务器登录一次即可访问这些系统中的任何一个,无须多次登录. 配置的步骤如下: 1.生成安全证书 Cas serve ...

  4. systemverilog interface杂记

    随着IC设计复杂度的提高,模块间互联变得复杂,SV引入接口,代表一捆连线的结构. Systemverilog语法标准,新引入一个重要的数据类型:interface. interface主要作用有两个: ...

  5. PHP面向对象程序设计之接口(interface)

    接口(interface)是抽象方法和静态常量定义的集合.接口是一种特殊的抽象类,这种抽象类中只包含抽象方法和静态常量. 为什么说接口是一种特殊的抽象类呢?如果一个抽象类里面的所有的方法都是抽象方法, ...

  6. centos、linux查找未挂载磁盘格式化并挂载?

    centos.linux查找未挂载磁盘格式化并挂载? df -h 查看当前linux服务器硬盘: fdisk -l /dev/sda   第一块硬盘 /dev/sdb   第二块硬盘 依此类推 以/d ...

  7. fabric生产环境代码包发布管理

  8. eclipse中设置新建jsp文件的编码格式

    每次新建jsp文件时,默认都是ISO-8859-1,每次涉及有中文的时候都得改成UTF-8,这就很麻烦了. 解决的方法就是,设置新建jsp文件的编码格式. 解决方法 结果 或者更改它的encoding

  9. Tomcat出现java.lang.Exception: Socket bind failed

    今天测试系统,Tomcat(apache-tomcat-6.0.20windows版)突然出现异常,导致JSP页面无法运行.错误提示如下:2014-3-12 17:13:29 org.apache.c ...

  10. Java多线程 - Callable和Future

    已知的创建多线程的方法有继承Tread类和实现Runnable方法.此外Java还提供了Callable接口,Callable接口也提供了一个call()方法来做为线程执行体.但是call()方法与r ...