Fan Trap 简析

BO Universe设计的时候对表关联的设计有两种情况需要注意和避免的: Chasm Trap和Fan Trap.

前者是在一种典型情况下,如一个Dimension同时与两个FCT表进行一对多连接的时候,如果FCT里面包含Measure和Dimension/Detial 对象的时候,select也包含Dimension,查询结果里面出现dimension的笛卡尔集,导致measure被错误的累计成一个很大的值: 如


(正常情况,对于Orders聚合结果应该使400, Loans聚合结果可能是150)

这种情况比较容易理解。 BO提供了多种方法解决:  
1)  对每个fact表建独立的context;  
2) 限制每个measure用独立的sql 查询;
3) 分universe, 一个universe对应一个fact;

后者稍微有点抽象(可能只是对我来说比较陌生):
如果一个dimension一对多连接另一个fact, 然后一个fact再以一对多方式连接第三个fact. 如下图:




此时对于第二层的fact表聚合就会出现FAN Trap,但这种情况只对贪心的人有效,比如你希望用一条SQL做聚合操作,得到两个fact表中measure信息:




自己设计一个例子:



DROP TABLE City purge;
DROP TABLE City_Type purge;
DROP TABLE Customers purge;

CREATE TABLE City
(
    ID                    INTEGER  NULL ,
    CUST_ID               INTEGER  NULL ,
    CITY_NAME             VARCHAR2(20)  NULL ,
    RENT_INCOME           INTEGER  NULL
);



CREATE TABLE City_Type
(
    ID                    INTEGER  NULL ,
    CITY_ID               INTEGER  NULL ,
    CITY_TYPE             CHAR(18)  NULL ,
    GDP_FROM_AREA         INTEGER  NULL
);



CREATE TABLE Customers
(
    ID                    INTEGER  NULL ,
    CUST_NAME             VARCHAR2(20)  NULL
);


insert into Customers values (1,'JEFF');
insert into Customers values (2,'LARRY');
insert into Customers values (3,'BILL');
INSERT INTO City values (1,1,'SH',1000);
INSERT INTO City values (2,1,'BJ',1500);
INSERT INTO City values (3,1,'SY',2500);
INSERT INTO City values (4,1,'SH',500);
INSERT INTO City values (5,2,'WH',1200);
INSERT INTO City values (5,2,'SH',600);
INSERT INTO City values (5,2,'SZ',100);
INSERT INTO city_type values (1,1,'LOV',1000000);
INSERT INTO city_type values (2,2,'LOV',1200000);
INSERT INTO city_type values (3,2,'LOV',1080000);
INSERT INTO city_type values (4,3,'LOV',1300000);
INSERT INTO city_type values (5,3,'LOV',1600000);
INSERT INTO city_type values (6,5,'LOV',900000);
INSERT INTO city_type values (7,6,'LOV',2000000);
INSERT INTO city_type values (8,7,'LOV',1100000);

--- 然后看下面的query:

SELECT
  a.CUST_NAME,
  sum(b.RENT_INCOME),
  sum(c.GDP_FROM_AREA)
FROM
CUSTOMERS a,
CITY b,
CITY_TYPE c
where a.id=b.cust_id
and b.id=c.city_id
and a.cust_name='JEFF'
group by a.cust_name;

CUST_NAME            SUM(B.RENT_INCOME) SUM(C.GDP_FROM_AREA)
-------------------- ------------------ --------------------
JEFF                               9000              6180000

9000 这个值显然是被重复累加过的结果,那他正确的情况应该是多少呢?看下面的查询:

SELECT
  a.CUST_NAME,
  b.RENT_INCOME,
  c.GDP_FROM_AREA
FROM
CUSTOMERS a,
CITY b,
CITY_TYPE c
where a.id=b.cust_id
and b.id=c.city_id
and a.cust_name='JEFF'
--group by a.cust_name;





CUST_NAME            RENT_INCOME GDP_FROM_AREA
-------------------- ----------- -------------
JEFF                        1000       1000000
JEFF                        1500       1080000
JEFF                        1500       1200000
JEFF                        2500       1600000
JEFF                        2500       1300000

5 rows selected.

1500和2500重复了2次,所以正确结果应该是1000+1500+2500 = 5000.

所以FAN Trap实际上就是context中间表中包含measure,而且又是一对多的方式被dimension连接.

而解决FAN Trap这个问题的方法也比较简单,即对每个measure使用多重sql,但这个方法并不适用于维对象,如果是这种情况最好考虑使用多重universe的方法。