最近在处理数据的时候遇到了一些比较典型的问题,想想其他做BI的朋友可能也会遇到,因此想趁热打铁,把这些解决方案记下来,并且拿出来分享下,也省掉大家许多时间,发扬下拿来主意的精神吧。不过作为程序员必要的思考还是需要的,不然哪里来的进步呢:D 好,闲话少叙,来点干货。 现在大的Cube,基本都要存储几年的数据,但是一旦数据量膨胀的过快,Cube的处理就会很慢,因此最先想到的就是在尽量不丢失数据粒度的和降低报表质量的前提下减少数据行数。简单的方案就比如,现在需要存储5年的数据,经过跟客户的协商,最近两年的数据是daily的,剩下3年的历史存储Weekly粒度的,这样数据量就大大减少。随之而来的问题就是一些Measure(度量值),需要根据数据粒度去分别计算,这样就需要区分数据到底是daily,还是weekly的。 简单的想法就是,判断fact数据是否在日期维度上是连续的。比如daily数据,如果当前天有记录的话,那么前一天有记录,或者后一天有记录,就认为这是daily的,反之就是weekly的数据。 假设现在有日期维度[Calendar],但是又遇到问题了,用什么样的Measure,来判断是不是有数据呢,毕竟有很多的Measure都是稀疏的啊。这时候就需要根据你的系统里的具体业务来确定了,举个常见的例子,超市每天都有POS的数据,那么只要看POS Sales就可以确定当天是不是有记录了。 因为很多的Measure都要根据数据粒度重新计算,所以最好将这些放在MDX Script里面做定义,相应的MDX脚本如下
1 CREATE HIDDEN SET CURRENTCUBE.[Daily Range] as 2 filter([calendar].[date].members , 3 (([Calendar].[Date]. currentmember ,[Measures].[POS Sales])>0) 4 and (aggregate(head ( 5 nonempty( 6 [Calendar].[Date]. currentmember.nextmember , 7 [Measures].[POS Sales]) )>0 or 8 aggregate(Tail ( 9 nonempty( 10 [Calendar].[Date]. currentmember.prevmember , 11 [Measures].[POS Sales]) )>0) 12 );
上面的MDX中使用了aggregate是为了处理不用业务中核心Measure的聚合类型不同,从而是这个脚本更加通用,如果核心的Measure换掉了,而且聚合方式不一样的话,只要直接替换Measure就可以了,使用Tail(),Head(),是为了适应在不同维度中的情况,假如需要看其他维度上的数据是否连续就可以直接套用上面的语句。
上面的还只是准备工作,既然找到的Daily数据的区间范围就自然能够确定Weekly的范围了,这样两个set就算是求出来了:
1 CREATE HIDDEN SET CURRENTCUBE.[DS] as 2 {head([Daily Range]). item(0):tail ([Daily Range]).item(0)}; 3 CREATE HIDDEN SET CURRENTCUBE.[WS] as //for weekly data stream 4 {head([calendar].[date]. children).item (0):tail([Daily Range]).item(0).prevmember };
完成了上面两步你的工作,其实还没有结束,剩下的工作就是重新定义你的Measure,本例中是基于Calendar计算的,那么就需要创建一个Calendar Fact的Measure Group,然后定义一些dummy的measure,接下来就是替换这些measure的工作了。 比如要计算相应的销售天数,可以定义个在Calendar Fact上定义一个[Sales Day Count],基于sum聚合的,然后替换如下
([WS],[Measures].[Sales Day Count])= ([Calendar].[Date].members,[Measures].[Sales Week Count])
红色标记的部分就需要根据具体的业务来设置相应的值了,不再赘述。
至于为什么要这样替换,留给各位自己思考啦,程序员还是需要思考的:D