olap分析平台的设计与实现(十八)- 表单布局的概念

本文详细介绍了OLAP分析平台的设计与实现过程,包括维度数据准备、表单布局概念解析及数据仓库模型构建。涵盖维度数据抽取、管理与mondrian引擎应用,以及分析表单的多层次查询实现。

开发原型需要的维度数据:

开发先做关键原型,我们先准备各种原型需要的数据。

先准备维度数据:年度、版本、收费项目、区划

                                                                             (维度的管理类) 

一个具体维度,数据库上有三张表反映:

                                                                             (维度及mondrian相关表) 

维度相关的表:

  1. commObject,所有原始的祖先表;
  2. 前缀为 “c_” 的表,用于管理各类维度。
  3. 前缀为““c_dim_”的表,是mondrain引擎用到维度表。

配置mondrian的schemal相关的维度表用"c_dim_"前缀开头。

维度数据准备,以后应该用kettle抽取?

由于我先搞了一个项目维度表,感觉内容多了,相关数据重新迁移到commobject表。

  select p.projectkey + 500000,
         p.projectname,
         1,
         -100,    --上级id 下面再处理
         case   --代数
           when p.projectcode = p.level1projectcode then
            1
           when p.projectcode = p.level2projectcode then
            2
           when p.projectcode = p.level3projectcode then
            3
           when p.projectcode = p.level4projectcode then
            4
           when p.projectcode = p.level5projectcode then
            5
           when p.projectcode = p.level6projectcode then
            6
           when p.projectcode = p.level7projectcode then
            7
           when p.projectcode = p.level8projectcode then
            8
         end,
         
         case p.isleaf   --是否有子节点
           when '0' then
            1
           when '1' then
            0
         end,
         rank,
         0,
         to_timestamp(to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss'),     --时间戳
                      'yyyy-mm-dd hh24:mi:ss')
    from dim_chargeproject p,
         
         (select p.projectkey projectkey,
                 p.projectcode,
                 p.projectname
                 
                ,
                 rank() over(order by p.projectcode) rank   --排序号 可能有问题
            from dim_chargeproject p) pr
   where pr.projectkey = p.projectkey

ps:timestamp 是oracle对date的扩展,其度量精度比date类型更高。

case when 在这条语句中有2个形态,注意语法上些许差别。

上面没有更新commobject表中parent,后面用一条语句更新一下,即根据一张表的数据更新另外一张表相关字段。

--temp22 是一张临时表 
update commobject t

   set t.parent = (select s.pk2 from temp22 s where s.pk0 = t.id)

 where t.id in (select s.pk0 from temp22 s)

mondrian用到的维度表是偏平化结构,所以要去掉非末级节点:


delete   from c1_dim_account  c1 where c1.id in (

select p.projectkey+500000 pk0 from dim_chargeproject p where p.isleaf=0);

用于管理目的的维度表和mondrian引擎用到的维度表有什么区别呢?

mondrian用到的维度表是偏平化的,管理用的维度需要方便反映上下级层级结构。

为方便后期开发,我们把维度数据存放在2个表中,2个表其实反映的是同一类维度信息。 

到此,模拟维度数据准备完毕。

分析表单布局的概念:

第一小节,我们已经介绍过布局的概念,这里以图再说明如下:

                                                                  (假如对维度不进行分组) 

                                                (上图中,分组的情况有7个布局 (不含页面、视点相关布局))

 页面、视点可以看成另外隐藏在表单背后的维度的行、列(不是准确描述)。

其余参考我前面写的:非税olap分析平台的设计与实现(一)--数据仓库模型中,相关内容。

整体而言,查询分为三个层次,1)布局类型上的查询、2)特定布局类型、特定组上的查询  3)特定布局查询

获取特定表单 特别布局类型上的List<List<List<DimMember>>> getFormMbrsAsLayoutGroup(formId,layoutType...);

获取特定表单 特别布局类型 特定组     List<List<DimMember>> tempResult = getFormMbrsAsLayout(formId,layoutType,groupId......);

特定布局上的成员的查询:

     这样分2种情况:

    List<DimMember> layoutMbrs =getMemberWithQueryType(...)  这种情况不展开查询函数;

   

     List<DimMember> layoutMbrs =getMembersByQueryType(...)  这种情况展开查询函数;

实现(由顶层到细节):

1、取得行/列上的集合:

List<List<List<DimMember>>> dimMembers_col = getFormMbrsAsLayoutGroup(form, MConstants.LAYOUT_COL, true);

2、取得一个布局类型的组上的集合,这里布局类型指的是行、列、页面、视点,关键参数为 form、维度类型、组号:

List<List<DimMember>> tempResult = getFormMbrsAsLayout(form,
                    layoutType, groupPosition, isOpenQueryType);

3、取得一个具体布局类型上 特定组的的布局,由具体布局,获得dimession,.另外一个方法是:考虑到所有组的维度dimesssion 是否也可以根据行列类型,获取所有组的dimession,关键参数为 form、维度类型、组号

List<CFormLayout> getAxisFormLayoutList(int formId,
												   int groupPosition, int layoutType)

4、上一步获取到的是 :一个特定表单、特定布局类型、特定组上布局信息,考虑到在一个表单 的特定布局上,所有组的类 布局信息是一样的,应该也有其他办法获取相关数据。现在遍历上一步获取到的布局信息,获取一个组内的所有维度成员信息。

List<List<DimMember>> mbrs = new ArrayList<List<DimMember>>();
......
List<DimMember> layoutMbrs = new ArrayList<DimMember>();       
 for (int i = 0; i < layouts.size(); i++) {
            //取得一个特定布局(特定组、特定组内特定的布局)的成员集合List<DimMember>
            layoutMbrs = getLayoutMbrs(form.getCubeId(), layouts.get(i),
                    isOpenQueryType);
            mbrs.add(layoutMbrs);
        }

5、获取一个布局上,具体维度成员集合:

layoutMbrs = getLayoutMbrs(form.getCubeId(), layouts.get(i),
                    isOpenQueryType);

这一步,逻辑略复杂。

首先是要获取特定布局上一个维度成员的集合,然后对这个结果进行相关处理::

List<CFormMbr> formMbrs = getFormLayoutMembersByLayoutId(layout.getId());

6、getFormLayoutMembersByLayoutId方法中,通过调用getFormLayoutMembersByLayoutId获取维度成员集合,getFormLayoutMembersByLayoutId方法的实现:

	/**
	 * 通过布局ID获取布局成员列表
	 * 
	 * @param layoutId
	 * @return
	 */

	@Override
	public List<CFormMbr> getFormLayoutMembersByLayoutId(int layoutId) {
		List<CFormMbr>  cFormMbrs=new ArrayList<CFormMbr>();
//方法一 :从数据库当中获取数据
		//CFormMbr fomMmeber = new CFormMbr();
//		fomMmeber.setLayoutId(layoutId);
//		Example<CFormMbr> example = Example.of(fomMmeber);
//		List<CFormMbr> formMembers=formMbrMapper.findAll(example);
//		return formMembers;

		//方法二:从缓存当中获取这个数据
		try {
			//cFormMbrs=(List<CFormMbr>)cacheHelp.getObjectInCache(Constants.C_FORMMbr,new C_FormMbr_layoutId_PK(false),layoutId);
			cFormMbrs=(List<CFormMbr>)cacheHelp.getObjectInCache(CFormMbr.class,new C_FormMbr_layoutId_PK(false),layoutId);
		} catch (CacheException e) {
			e.printStackTrace();
		}
		return cFormMbrs;
	}

7.getFormLayoutMembersByLayoutId方法中,对特定布局的成员信息(CFormMbr),进行遍历处理:

        for (int i = 0; i < formMbrs.size(); i++) {
            boolean isCompare = false;  //?
            DimMember member = null;
            memberId = formMbrs.get(i).getMemberId();
            queryType = formMbrs.get(i).getQueryType();
            if (isOpenQueryType) {   //展开的情况
                if (formMbrs.get(i).getType() == MConstants.FORM_MBR_TYPE_VAR) {
                    //todo
                      .....
                } else {
                    tempMembers = memberEval.getMembersByQueryType(queryType, memberId, cubeId, layout.getDimId());
                    if (tempMembers != null && tempMembers.size() > 0) {
                        Iterator<DimMember> memberIterator = tempMembers.iterator();
                        while (memberIterator.hasNext()) {
                            DimMember beAddM = memberIterator.next();
                            boolean isHas = false;
                            for (DimMember temp : members) {
                                if (temp.getMemberId() == beAddM.getMemberId()) {
                                    isHas = true;
                                    break;
                                }
                            }
                            if (isHas) {
                                memberIterator.remove();
                            }
                        }
                    }
                    members.addAll(tempMembers);
                }
            } else {  //closed node

                .....
            }
......
        }

8.下面,我们只讨论节点是全展开,且是普通节点的情况,关键是下面的的方法,根据memberId 和 query Type,返回相应节点集合,如这个节点quey type是“子代包含”则把相应节点全部返回。 

tempMembers = memberEval.getMembersByQueryType(queryType, memberId, cubeId, layout.getDimId());

9.memberEval.getMembersByQueryType方法中,首先要把CFormMbr转为DimMember对象:

DimMember curMember=getMemberInDimByMemberId(memberId, dimId);

具体实现(方法一:从数据库中查的方式):

	/**
	 * 获取维度成员
	 * @param memberId
	 *  维度成员ID
	 * @param dimId
	 *            维度ID
	 * @return
	 */
	@Override
	public DimMember getMemberInDimByMemberId(int memberId, int dimId) {
			DimMember object=null;
			CDimension dimension = getDimInstanceById(dimId);
			int obj_type=dimension.getObjType();
		switch (obj_type) {
			case MConstants.DIM_ACCOUNT:
				object =accountMapper.getOne(memberId);
				break;
			case MConstants.DIM_ENTITY:
				object =entityMapper.getOne(memberId);
				break;
			case MConstants.DIM_YEAR:
				object =yearMapper.getOne(memberId);
				break;
			case MConstants.DIM_PERIOD:
				object =periodMapper.getOne(memberId);
				break;
			case MConstants.DIM_VERSION:
				object =versionMapper.getOne(memberId);
				break;
			case MConstants.DIM_SCENARIO:
				object=scenarioMapper.getOne(memberId);
				break;
			case MConstants.DIM_CURRENCY:
				object =curencyMapper.getOne(memberId);
				break;
		}
			return object;
	}

从缓存中查的方式:略

定义一张分析表单,需要用到的数据:

1、定义一个表单,当然需要表单本身信息(如名称、说明等);

2、关联的布局信息。

准备事实数据:

insert into c_fact
  (project, year, org, version, v)
  select CHARGEPROJECT_KEY + 500000 account,
         case year_key
           when 16 then
            60201
           when 17 then
            60202
           when 18 then
            60203
           when 19 then
            60204
         end,
         
         421087,
         60253,
         amt
    from FACT_COL_TRADE_DETAIL f
   where f.year_key in (16, 17, 18, 19)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值