众所周知,BIEE的OLAP主要可以分为两种,一种是基于Essbase等多维数据库的MOLAP,一种是基于关系数据库的ROLAP.
由于存储结构上的不同导致这两种OLAP在呈现没有事实的维度组合时有着明显的不同.
MOLAP对于没有事实的维度组合会呈现为空,具体表现为,会将维度成员的组合记录呈现出来,对应的事实指标显示为空;
ROLAP对于没有事实的维度组合则压根不会呈现,具体表现为,不会将没有事实的维度成员组合记录呈现出来.(除非你在事实表中插入了空事实)
对于ROLAP的这种方式往往不能满足业务用户的需求,对于没有事实的维度组合,业务用户要求以要将他们呈现出来,以明确的告诉他们,没有这类事实发生!
那我们应该怎么办呢?
我们首先想到的可能是用左联接,即在逻辑层把维表和事实表的连接关系设置为左连接.很遗憾,这种方式对于单张维表来说是可行的,多张的话就不行了;另外,如果你设置为了左连接,那么所有的用于这个模型的地方都将是左连接,而大部分却是不需要的,这会带来严重的性能问题.(具体相关缘由请大家自行验证)
那是不是我们就没有办法了呢?我们先抛开BIEE本身来思考这个问题.让我们自己写SQL的话,我们会怎么办?
也许我们会这样做
1:首先用维表和事实表关联,并加上过滤条件查出我们需要的数据;得到结果集A
2:求需要呈现的维度的笛卡尔集,并加上过滤;得到我们关注的维度组合B
3:用B外连A得到我们要的结果!
OK,这样是比较优秀的做法,那能在BIEE中实现吗?
答案当然是肯定的了!
下面我们以BIEE 11g官方的SampleApp demo为例讲解实现方法。
注:此法同样适用于10g
上面的步骤1就不用实现了,我们直接开始步骤2吧
在物理层新建一张视图,命名为FactCrossJoin,代码如下
注:连接条件均为1=1,这样是为了采生笛卡集的效果
接下将FactCrossJoin的Preserver字段拖到逻辑层的F0 Sales Base Measures表中,并将其聚合算法设置为max,
并将其名字改为Preserve Dimension(做到见名知意)
最后将Preserve Dimension字段拖到展现层,暴露给前端。
至此,模型就建完了,下面的问题是我们怎么用。
先来看看普通情况吧,2008年每周各类产品的销售如下:
可以明显的看到,并不是所有种类的产品在这三周都有销售的,而我们的需求是:
不管某类产品有没有销售都需要将他们呈现出来,以明显的告诉用户哪些产品没有销售,也就是如下图所示:
上图是怎么出来的呢?很简单,直接把我们前面在RPD中新增的Preserve Dimension字段拖进来即可!
至此大功告成!
当然了,我们肯定不想Preserve Dimension这个字段出现在报表里,我们有两种办法,
一种是将其添加到过滤器里并赋予Yes的值;另一种是将该字段隐藏其来。
将该字段添加到过滤器里有个好处是,我们可以把这种功能提供给客户来选择,有的客户想看所有的数据;而有的客户则想压缩空行。
下面我们来检查一下这种做法生成的SQL吧。
WITH SAWITH0 AS (select sum(T42433.Revenue) as c1, T42409.Type as c2, T42404.Per_Name_Week as c3 from SAMP_PRODUCTS_D T42409 /* D10 Product (Dynamic Table) */ , SAMP_TIME_DAY_D T42404 /* D01 Time Day Grain */ , SAMP_REVENUE_F T42433 /* F10 Billed Rev. */ where ( T42404.Calendar_Date = T42433.Bill_Day_Dt and T42404.Per_Name_Year = '2008' and T42409.Prod_Key = T42433.Prod_Key ) group by T42404.Per_Name_Week, T42409.Type), SAWITH1 AS (select max(T198986.Preserver) as c1, T42409.Type as c2, T42404.Per_Name_Week as c3 from SAMP_PRODUCTS_D T42409 /* D10 Product (Dynamic Table) */ , SAMP_TIME_DAY_D T42404 /* D01 Time Day Grain */ , (SELECT 'Yes' Preserver FROM dual) T198986 where ( T42404.Per_Name_Year = '2008' ) group by T42404.Per_Name_Week, T42409.Type), SAWITH2 AS (select D1.c1 as c1, D1.c2 as c2, D1.c3 as c3, D1.c4 as c4, D1.c5 as c5 from (select 0 as c1, case when D1.c2 is not null then D1.c2 when D2.c2 is not null then D2.c2 end as c2, case when D1.c3 is not null then D1.c3 when D2.c3 is not null then D2.c3 end as c3, D1.c1 as c4, D2.c1 as c5, ROW_NUMBER() OVER (PARTITION BY case when D1.c2 is not null then D1.c2 when D2.c2 is not null then D2.c2 end , case when D1.c3 is not null then D1.c3 when D2.c3 is not null then D2.c3 end ORDER BY case when D1.c2 is not null then D1.c2 when D2.c2 is not null then D2.c2 end ASC, case when D1.c3 is not null then D1.c3 when D2.c3 is not null then D2.c3 end ASC) as c6 from SAWITH0 D1 full outer join SAWITH1 D2 On D1.c2 = D2.c2 and D1.c3 = D2.c3 ) D1 where ( D1.c6 = 1 ) ) select D1.c1 as c1, D1.c2 as c2, D1.c3 as c3, D1.c4 as c4, D1.c5 as c5 from ( select D1.c1 as c1, D1.c2 as c2, D1.c3 as c3, D1.c4 as c4, D1.c5 as c5 from SAWITH2 D1 order by c1, c3, c2 ) D1 where rownum <= 1000001
可以看到,的确是按照我们的想法生成的。为什么会这么样呢?实际上这就是我们常说的多事实表有公共维度的情况;
首先是维度分别和事实表关联得到结果集A、B;然后在对A、B使用公共维度字段连接起来(可以看到这里使用的是全连接)
那么在这里我留下一个问题,如果这两张事实表在存在公共维度的情况下还有非公共维度应该怎么办呢?
欲知后事如何,请听下回分解。