olap分析平台的设计与实现(四)--mondrian schemal

开源OLAP引擎:Mondrian OLAP Created 星期四 20 十月 2016为了满足业务管理和决策的报表系统(包括传统报表、数据仓库、OLAP等)也被创建出来,企业主管通过报表了解企业的总体运行状态。但是,随着企业间竞争的加剧和市场节奏的进一步加快,企业的日常管理需要对关键业务指标的更加实时的监控和反馈。比如:制造业需要更及时的仓库调度、金融业需要更实时的风险防范、电信业需要更及时的服务指标监控。于是,越来越多的企业 阅读详情

本文重点总结mondrian4 schemal的写法及其趟过的各种坑,我的mondrian版本是4.7.0.0-12

不涉及雪花模型、共享维、Virtual cubes,attribute的property、Closure tables、聚合、调优 等,总结最基础的schemal,毕竟财政业务相对是比较简单的。

olap发展一直不太好,我觉得很重要的原因就是schemal等的原因,再加上展示麻烦。mdx很容易,弄过sql的人,搞mdx倒是蛮快的。

3.X的schemal:可参考 BI中如何配置模式文件(Schema)一文

和数据库连接弄好了,我们当然是为了查询需要的数据。我们先利用OLAP4J的query对象的,进行相关查询。

查询前我理解最好校验一下,olap4j文档上说,校验做了3件事情:

1)Performs default selections 执行默认选择

2)Validates the presence of at least one dimensions on both ROWS and COLUMNS

       axis  校验在行、列轴上是否存在至少一个维度

3)Tries to build a MDX parse tree 尝试构建MDX分析树

我们开始Creating selections to include or exclude from a query:

做这件事情,其实是2步:

1、找基准点

2、基于基准点进行操作

        // olap4j的mdx接口,是一个jdbc实现,推荐
        System.out.println("\r\n===================olap4j接口==================");
        String mdxStr = "select {[年].Members} on columns,{[组织].Members} on rows from 模型一";
        // 建立连接
        String strUrl = "jdbc:mondrian:";
        strUrl += "Jdbc=" + url;
        strUrl += ";JdbcUser=" + userName;
        strUrl += ";JdbcPassword=" + password;
        // strUrl += ";Catalog=" + xmlFile;
        strUrl += ";CatalogContent=" + getCatalogContent();
        Class.forName("mondrian.olap4j.MondrianOlap4jDriver");
        Connection olap4jConn = DriverManager.getConnection(strUrl);
        OlapConnection olapConn = (OlapConnection) olap4jConn.unwrap(OlapConnection.class);
        NamedList<Cube> cubes =
                olapConn.getOlapSchema().getCubes();
        Cube cube = cubes.get("模型一");
        Query myQuery =
                new Query("model1Query", cube);
        myQuery.execute();
       System.out.println("------------myQuery------------:"+myQuery);
        QueryDimension year = myQuery.getDimension("Year");
        QueryDimension entity = myQuery.getDimension("Entity");
        myQuery.getAxis(Axis.COLUMNS).addDimension(year);
        myQuery.getAxis(Axis.ROWS).addDimension(entity);
        Member year2017 =
                cube.lookupMember(
                        IdentifierNode.ofNames ("Year", "2017年")
                                .getSegmentList());
        year.include(year2017);
        myQuery.validate();
        System.out.println(
                myQuery.getSelect().toString());

        String s=myQuery.getSelect().toString();
        System.out.println("s:"+s);

        CellSet cellSet =   myQuery.execute();
        // 输出结果
        CellSetFormatter formatter = new RectangularCellSetFormatter(false);
        formatter.format(cellSet, new PrintWriter(System.out, true));

执行结果:

SELECT
{[Year].[2017年]} ON COLUMNS,
{[Entity].[All Entitys]} ON ROWS
FROM [模型一]
s:SELECT
{[Year].[2017年]} ON COLUMNS,
{[Entity].[All Entitys]} ON ROWS
FROM [模型一]
10:13:36.394 [mondrian.rolap.RolapResultShepherd$executor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-COUNT(DISTINCT"YEAR") is of internal type INT. JDBC type was 2.  Column precision=0.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-c0 is of internal type OBJECT. JDBC type was -9.  Column precision=20.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-c0 is of internal type OBJECT. JDBC type was -9.  Column precision=20.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-m0 is of internal type OBJECT. JDBC type was 2.  Column precision=0.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-c0 is of internal type OBJECT. JDBC type was -9.  Column precision=20.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-c0 is of internal type OBJECT. JDBC type was -9.  Column precision=20.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-m0 is of internal type OBJECT. JDBC type was 2.  Column precision=0.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-c0 is of internal type OBJECT. JDBC type was -9.  Column precision=20.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-c0 is of internal type OBJECT. JDBC type was -9.  Column precision=20.  Column scale=0
10:13:36.462 [mondrian.rolap.agg.SegmentCacheManager$sqlExecutor_2] DEBUG mondrian.spi.impl.JdbcDialectImpl - JdbcDialectImpl.getType Dialect- ORACLE, Column-m0 is of internal type OBJECT. JDBC type was 2.  Column precision=0.  Column scale=0
|             | 2017年       |
+-------------+-------------+
| All Entitys | 79440368.57 |

为进一步测试,我写4个测试程序:

测试程序测试foodmart例子。(用mondrian与olap4j分别实现)

一个测试程序测试非税(用mondrian与olap4j分别实现);

用mondrian4的规范重写一遍非税与foodmart的schemal。

再原来的3.0版本基础上,重写了,报错:

RolapSchema$MondrianSchemaException: Dimension 'Year' not found in this cube, but there is a schema dimension of that name. Did you intend to create cube dimension referring to that schema dimension? (in ForeignKeyLink) (at line 42, column 10)

原来可能是是3.0引用方式,4.0中schemal不认识它们?

动手按4.0文档的内容重新写schemal如下:

<?xml version="1.0" encoding="UTF-8"?>
<Schema name="OlapSchema" metamodelVersion="4.0">

  <PhysicalSchema>
    <Table name="C1_FACT"/>
    <Table name="C1_DIM_YEAR"/>
    <Table name="C1_DIM_ACCOUNT"/>
    <Table name="C1_DIM_ENTITY"/>
    <Table name="C1_DIM_PERIOD"/>
  </PhysicalSchema>

  <Cube name="模型一" defaultMeasure="金额">


    <Dimensions>
      <Dimension name="Year"  table="C1_DIM_YEAR" key="Id" >
        <Attributes>
          <Attribute  name="Id"  keyColumn="ID"  hasHierarchy='false'/>
           <Attribute  name="YearName"   hasHierarchy='false'>
             <Key>
               <Column name="YEAR"/>
             </Key>
          </Attribute>
          <!--和上面是等价的写法-->
          <!--<Attribute  name="YearName"   keyColumn="YEAR" hasHierarchy='false'/>-->

        </Attributes>
        <Hierarchies>
          <Hierarchy name="YearLy" hasAll="true">
            <Level attribute="YearName"/>
          </Hierarchy>
        </Hierarchies>
      </Dimension>

      <Dimension name="Period" table="C1_DIM_PERIOD"  key="Id">
        <Attributes>
          <Attribute  name="Id"  keyColumn="ID" columnName="ID" />
          <Attribute  name="Year"  keyColumn="ID" columnName="YEAR" />
          <Attribute  name="Quarter"  keyColumn="ID" columnName="QUARTER"  />
          <Attribute  name="Month"  keyColumn="ID" columnName="MONTH"/>
        </Attributes>
        <Hierarchy name="Period" hasAll="false" defaultMember="[Period].[全年合计].[4季].[12]" >
          <Level attribute="Year"/>
          <Level attribute="Quarter"/>
          <Level attribute="Month"/>
        </Hierarchy>
      </Dimension>

      <Dimension name="Entity" table="C1_DIM_ENTITY" key="Id">
        <Attributes>
          <Attribute  name="Id"  keyColumn="ID"  hasHierarchy='false'/>
          <!--以下写法逻辑上是否是错误的?-->
          <!--方法1-->
<!--          <Attribute  name="Province" keyColumn="LEVEL_1" hasHierarchy='true'>
          </Attribute>-->
          <!--方法2-->
           <!-- <Attribute  name="Province"  keyColumn="ID" columnName="LEVEL_1" hasHierarchy='true'/>-->
          <!--方法3-->
          <Attribute  name="Province"  column="LEVEL_1" hasHierarchy='true'>
            <Key>
              <Column table="C1_DIM_ENTITY" name="ID"/>
              </Key>
          </Attribute>


          <Attribute  name="City" keyColumn="ID" columnName="LEVEL_2"  hasHierarchy='true'>
          </Attribute>
          <Attribute  name="Country" keyColumn="ID" columnName="LEVEL_3"  hasHierarchy='false'>
          </Attribute>
        </Attributes>

        <Hierarchies>
          <Hierarchy name="EntityLy" hasAll="true">
            <Level attribute="Province"/>
            <Level attribute="City"/>
            <Level attribute="Country"/>
          </Hierarchy>
        </Hierarchies>
      </Dimension>
    </Dimensions>

    <MeasureGroups>
      <MeasureGroup name="C1_FACT" table="C1_FACT">
        <Measures>
          <Measure name="金额" column="V_1" aggregator="sum" visible="true" formatString="0.########"/>
        </Measures>
        <DimensionLinks>
          <ForeignKeyLink dimension="Year" foreignKeyColumn="YEAR"/>
          <ForeignKeyLink dimension="Entity" foreignKeyColumn="ENTITY"/>
          <ForeignKeyLink dimension="Period" foreignKeyColumn="PERIOD"/>
        </DimensionLinks>
      </MeasureGroup>
    </MeasureGroups>
  </Cube>
</Schema>

抛出错误:

Exception in thread "main" mondrian.rolap.RolapSchema$MondrianSchemaException: Attribute must have a key (in Attribute 'YearName') (at line 16, column 10)

shcemal中,属性修改如下:

<Attribute  name="Id"  keyColumn="ID"  hasHierarchy='false'/>,就不报告这个错误了。(上面已经改过了,原来写的column=)

另外,上面2个红框内容要一致。否则,报错如下:

 Key attribute 'ID' is not a valid attribute of this dimension (in Dimension 'Entity') (at line 27, column

schemal写好了,olap4j query 成功。

用mdx再测试一遍:

String mdxStr = "select {[Year].Members} on columns,{[Entity].Members} on rows from 模型一";

报错:

mondrian.olap.MondrianException: Mondrian Error:The 'Entity' dimension contains more than one hierarchy, therefore the hierarchy must be explicitly specified.

修改mdx:

String mdxStr = "select {[Year].Members} on columns,{[Entity].[EntityLy].[湖北省财政厅]} on rows from 模型一";

ok!结果如下:

写4.0schemal,我是基于2个文档的:

一个是4.0官方文档

一个是Mondrian 4规范文章(网上搜的)

进一步用Query对象查询前,我们先总结一下mondrain4.0schemal。

整体结构:见官方文档的2.1 Structure of a Schema 

A cube (see <Cube>) is a named collection of dimensions and measures.

measure:

  schemal中,别名定义:可以用于用户自定义属性?

  measure当中的caption属性,可以用于返回标题?

  table中,可以加入计算列,度量定义中,可以使用计算列。

  • An attribute is a value, possessed by every fact, by which you can divide facts into subsets. For example, you might wish to break down product sales by their color, the gender of the customer, and the store in which the product was sold; color, gender, and store are all attributes.
  • A dimension is a grouping of related attributes. For example, name, gender, zip code and eye color are attributes of the customer dimension; color, weight, and manufacturer are attributes of the product dimension.

属性的 key column then <Key> and keyColumn are equivalent

<key>元素和 attribute的属性 keyColumn,是等价的,看个人偏好 ?这个没有理解,反正key就是为和数据库匹配吧,如果不写keycolum,总报错。

对这个事情没有把握?那就用foodmart数据库重新写一个schemal

4.0文档的例子,也是报错Attribute must have a key ...

文档中是:

<Attribute name="Month" column="month_of_year">

<Key>

<Column name="the_year"/>

<Column name="month_of_year"/>

</Key>

</Attribute>

修改后是:

<Attribute name="Month" keyColumn="MONTH_OF_YEAR"/>

官文中间的<key>是啥作用,不是很了解。

官文中说:

Variations in syntax between different databases is handled by specifying more than one <Key> element:

和数据库有关?

For example, if you are defining an attribute with a simple (non-composite) key you could either write

<Attribute name="A">

<Key>

<Column name="c">

</Key>

</Attribute>

or

<Attribute name="A" column="c"/>

a simple (non-composite) key 的确是这样的。

复合键的写法如下:什么情况下用复合键呢,这个问题先留着。

If the attribute has a composite key, or if you wish to use the table attribute, you have to use the nested <Key> element:

<Attribute name="A">

<Key>

 <Column table="t1" name="c1"/>

 <Column table="t2" name="c2"/>

</Key>

</Attribute>

反正都和数据库有关。

attribute是什么意思呢,对我们的应用而言,它是必须的吗 我3.0的schemal没有写一个attribute呢。这个好像是3.0和4.0的一个明显不同的的地方。

官文推荐先写attribute,attribute的先后顺序也是查询结果的先后顺序?

看看文档的解释:

Parent elementAttributeEquivalent nested elementDescription
<Attribute>keyColumn<Key>Column(s) that comprise the key of this attribute.构成该属性关键字的列(数据库当中的字段)。
<Attribute>nameColumn<Name>Column that defines the name of members of this attribute. If not specified, the attribute key is used.定义此属性成员名称的列。如果未指定,则使用属性键。Column就是数据库字段名?
<Attribute>orderByColumn<OrderBy>Column(s) that define the order of members. If not specified, the attribute key is used.
<Attribute>captionColumn<Caption>Column that forms the caption of members. If not specified, the attribute name is used.
<Measure>column<Arguments>Column(s) that are the arguments to this measure. (When this measure is implemented by generating a SQL expression, these columns become the argument to the SQL aggregate function.)
<Table>keyColumn<Key>Column(s) that form this Table's key.
<Link>foreignKeyColumn<ForeignKey>Column(s) that form a foreign key from this Link's referencing table to its referenced table.
<ForeignKeyLink>foreignKeyColumn<ForeignKey>Column(s) that form a foreign key from a measure group's fact table to a dimension table.

keyColumn与nameColumn:我理解都是数据库字段,区别在于,一个是key column,一个就是这个属性的column

Attribute name must be specified 这个错误的解决:

RolapSchema$MondrianSchemaException: Attribute name must be specified. (Name can only be omitted when key contains a single column.) (in Attribute 'Province')

原因是我的配置文件:

          <Attribute  name="Province" column="LEVEL_1" hasHierarchy='true'>
           <Key>
              <Column name="ID"></Column>
              <Column name="LEVEL_1"></Column>
            </Key>
        </Attribute>

测试了三种写法,都可以解决这个报错:

          <!--以下写法逻辑上是否是错误的?-->
          <!--方法1-->
<!--          <Attribute  name="Province" keyColumn="LEVEL_1" hasHierarchy='true'>
          </Attribute>-->
          <!--方法2-->
           <!-- <Attribute  name="Province"  keyColumn="ID" columnName="LEVEL_1" hasHierarchy='true'/>-->
          <!--方法3-->
          <Attribute  name="Province"  column="LEVEL_1" hasHierarchy='true'>
            <Key>
              <Column table="C1_DIM_ENTITY" name="ID"/>
              </Key>
          </Attribute>

keyColumn到底绑定那个列呢,我感到有2种选择,实际出了问题再调整不迟,反正不是key 就是colum。,我目前的看法是:用key可以在数据库中确认唯一一条记录。

Atrr元素的columnName、column有啥区别呢,没明白?问题先留着。

Inherited attributes

Hierarchy是层次结构的重点:

By default, every hierarchy contains a top level called '(All)', which contains a single member called '(All {hierarchyName})'. This member is parent of all other members of the hierarchy, and thus represents a grand total.

默认情况下,每个层次结构都包含一个名为'(All)'的顶级,其中包含一个名为'(All {hierarchyName})'的成员。 该成员是层次结构中所有其他成员的父成员,因此代表总计。

If the <Hierarchy> element has hasAll="false", the 'all' level is suppressed. The default member of that dimension will now be the first member of the first level; for example, in a Time hierarchy, it will be the first year in the hierarchy. Changing the default member can be confusing, so you should generally use hasAll="true".

借用google翻译:

如果<Hierarchy>元素具有hasAll =“false”,则“全部”级别将被抑制。 该维度的默认成员现在将是第一级别的第一个成员; 例如,在时间层次结构中,它将是层次结构中的第一年。 更改默认成员可能会造成混淆,因此通常应使用hasAll =“true”。

You can also set the default member of a hierarchy explicitly. The defaultMember attribute should be the fully-qualified name of the default member.

<Hierarchy name="Yearly" hasAll="false" defaultMember="[Time].[1997].[Q1].[1]">

...

</Hierarchy>

When specified in this way, the default member can even be a calculated member.

这个对我们有点用,财务上经常有期初、期末的概念:

先动手在schemal中增加一段配置

      <Dimension  table="C1_DIM_PERIOD"  key="Id">
        <Attributes>
          <Attribute  name="Id"  keyColumn="ID"  />
          <Attribute  name="Year"  keyColumn="YEAR" />
          <Attribute  name="Quarter"  keyColumn="QUARTER" />
          <Attribute  name="Month"  keyColumn="MONTH" />
        </Attributes>
        <Hierarchy name="期间" hasAll="true" >
          <Level attribute="Year"/>
          <Level attribute="Quarter"/>
          <Level attribute="Month"/>
        </Hierarchy>
      </Dimension>

运行测试,报错:

Exception in thread "main" java.lang.NullPointerException
	at mondrian.olap.Util.quoteMdxIdentifier(Util.java:490)
	at mondrian.rolap.RolapSchemaLoader.getOrCreateDimension(RolapSchemaLoader.java:2703)

维度中添加:

再运行,报错:

.RolapSchema$MondrianSchemaException: table 'C1_DIM_PERIOD' not found (in Dimension 'Period') (at line 27, column 6)

检查,dimession有table属性,再看发现问题,修改如下:

  <PhysicalSchema>
    <Table name="C1_FACT"/>
    <Table name="C1_DIM_YEAR"/>
    <Table name="C1_DIM_ACCOUNT"/>
    <Table name="C1_DIM_ENTITY"/>
    <Table name="C1_DIM_PERIOD"/>
  </PhysicalSchema>

PhysicalSchema中增加C1_DIM_PERIOD的说明。

再运行,ok!

修改dimession:

        <Hierarchy name="Period" hasAll="false" defaultMember="[Period].[全年合计].[4季].[12]" >
          <Level attribute="Year"/>
          <Level attribute="Quarter"/>
          <Level attribute="Month"/>
        </Hierarchy>

修改了期间的默认值? 有什么用途,后面再说。

ps1:关于uniqueMembers的设置

uniqueMembers属性用于优化SQL生成。如果你已经知道在维表中level列的值在这个层次的父级是唯一的,可以设置uniqueMembers=”true”,否则设置为"false”。例如时间维[Year].[Month]在[Month]这个level,应该设置uniqueMembers=”false”,因为同样的月可以出现在不同的年。在另一方面,假设有[Product Class].[Product Name]层次,你可以确保[Product Name]是唯一的,就可以设置uniqueMembers=”true”。如果并不能确定,则先设置为false。一般在最顶级,经常是设置为true,因为没有父层次。

引用自:https://blog.youkuaiyun.com/wonder4/article/details/4833641 

ps2:level,到层次根有同样距离层次的成员的集合。

ps3:包含type属性的Level,值可以设置为String,Integer,Numeric,Boolean,Date,Time和Timestamp。默认是Numeric,因为主键列一般为数值类型。如果类型不同,Mondrian需要知道正确的类型以便生成SQL,例如String值会生成用单引号包含的语句.

我一直没有用type.mondrian3好像用的多,是不是因为现在的level都是引用的attribute?
 

mondrian4时间维度:

MDX相关时间的函数:

  • ParallelPeriod([level[, index[, member]]])
  • PeriodsToDate([level[, member]])
  • WTD([member])
  • MTD([member])
  • QTD([member])
  • YTD([member])
  • LastPeriod(index[, member])

ps:schemal中,表名、数据库列名全部是大写(oracle的原因);

schemal中,属性名称一般是首字母大写,其余小写。

本文结尾:我针对非税开票明细再写一个schemal:

事实表:fact_col_trade_detail,记录开票信息明细表,这个表记录了松滋2009年到2017年间全部开票信息。

我们只分析维度只选取收费项目、时间,度量维只选择金额、笔数,考虑到这个表本记录的事明细数据,每条记录就是一个项目一次收费情况,可以把笔数看成退化维度? 

不过,我这里先把这个也看成一个度量,增加一个字段no,  执行update fact_col_trade_detail t set t.no=1。

接下来,我来准备项目维度:

项目维度:维度表是: dim_chargeproject

时间维度:有2种思路:

一个参照foodmart例子,构建时间维度;

另外一个是把时间拆分为年度、期间2个维度;我们分别实现这2种维度:

dim_date:这个维度标中,最小日期是2005年,最大日期是2020年。

事实表中CHARGEDATE_KEY 与datakey相应。

另外一个时间维度体系是年度、与期间:这里先只搞个年度维度表DIM_YEAR_COLTRADE:和事实表中的year_key相关联。

下面是schemal:

<?xml version="1.0" encoding="UTF-8"?>
<Schema name="OlapSchema" metamodelVersion="4.0">

  <PhysicalSchema>
    <Table name="FACT_COL_TRADE_DETAIL">
    </Table>

    <Table name="DIM_CHARGEPROJECT">
      <Key>
        <Column name='PROJECTKEY'/>
      </Key>
    </Table>

    <Table name="DIM_DATE">
      <Key>
        <Column name='DATEKEY'/>
      </Key>
    </Table>
    <Table name="DIM_YEAR_COLTRADE">
      <Key>
        <Column name='YEARKEY'/>
      </Key>
    </Table>
  </PhysicalSchema>

  <Cube name="Col_trade_detail" defaultMeasure="金额">


    <Dimensions>

      <Dimension name="Year"  table="DIM_YEAR_COLTRADE" key='Year_key' >
        <Attributes>
          <Attribute name='Year_key' keyColumn='YEARKEY' hasHierarchy='false'/>
          <Attribute name='YearString' keyColumn='YEAR' hasHierarchy='false'/>
          <Attribute name='Yearname' keyColumn='YEARNAME' hasHierarchy='false'/>
        </Attributes>
        <Hierarchies>
          <Hierarchy name='YearLy' allMemberName='All Year'>
            <Level attribute='Yearname'/>
          </Hierarchy>
        </Hierarchies>
      </Dimension>
      <Dimension name='Time' table='DIM_DATE' type='TIME' key='Time Id'>
      <Attributes>
        <Attribute name='Year' keyColumn='THE_YEAR' levelType='TimeYears' hasHierarchy='false'/>
        <Attribute name='Quarter' levelType='TimeQuarters' hasHierarchy='false'>
          <Key>
            <Column name='THE_YEAR'/>
            <Column name='DATEOFQUATER'/>
          </Key>
          <Name>
            <Column name='DATEOFQUATER'/>
          </Name>
        </Attribute>
        <Attribute name='Month' levelType='TimeMonths' hasHierarchy='false'>
          <Key>
            <Column name='THE_YEAR'/>
            <Column name='MONTH_OF_YEAR'/>
          </Key>
          <Name>
            <Column name='MONTH_OF_YEAR'/>
          </Name>
        </Attribute>
<!--        <Attribute name='Week' levelType='TimeWeeks' hasHierarchy='false'>
          <Key>
            <Column name='the_year'/>
            <Column name='week_of_year'/>
          </Key>
          <Name>
            <Column name='week_of_year'/>
          </Name>
        </Attribute>-->
        <Attribute name='Day' levelType='TimeDays' hasHierarchy='false'>
          <Key>
            <Column name='DATEKEY'/>
          </Key>
          <Name>
            <Column name='DAY_OF_MONTH'/>
          </Name>
          <OrderBy>
            <Column name='DATEKEY'/>
          </OrderBy>
        </Attribute>
        <Attribute name='Month Name' hasHierarchy='false'>
          <Key>
            <Column name='THE_YEAR'/>
            <Column name='MONTH_OF_YEAR'/>
          </Key>
          <Name>
            <Column name='THE_MONTH'/>
          </Name>
        </Attribute>
        <Attribute name='Date' keyColumn='FULLDATE' hasHierarchy='false'/>
        <Attribute name='Time Id' keyColumn='DATEKEY' hasHierarchy='false'/>
      </Attributes>

        <Hierarchies>
          <Hierarchy name='Time' hasAll='false'>
            <Level attribute='Year'/>
            <Level attribute='Quarter'/>
            <Level attribute='Month'/>
          </Hierarchy>
        </Hierarchies>

      </Dimension>

      <Dimension name="Account" table="DIM_CHARGEPROJECT"  key="Projectkey">
        <Attributes>
          <Attribute  name="Projectkey"  keyColumn="PROJECTKEY" hasHierarchy='false'>
          </Attribute>
          <Attribute name='Level1projectname'  hasHierarchy='false'>
            <Key>
              <Column name='LEVEL1PROJECTCODE'/>
            </Key>
            <Name>
              <Column name='LEVEL1PROJECTNAME'/>
            </Name>
          </Attribute>
          <Attribute name='Level2projectname'  hasHierarchy='false'>
            <Key>
              <Column name='LEVEL1PROJECTCODE'/>
              <Column name='LEVEL2PROJECTCODE'/>
            </Key>
            <Name>
              <Column name='LEVEL2PROJECTNAME'/>
            </Name>
          </Attribute>
          <Attribute name='Level3projectname'  hasHierarchy='false'>
            <Key>
              <Column name='LEVEL1PROJECTCODE'/>
              <Column name='LEVEL2PROJECTCODE'/>
              <Column name='LEVEL3PROJECTCODE'/>
            </Key>
            <Name>
              <Column name='LEVEL3PROJECTNAME'/>
            </Name>
          </Attribute>
          <Attribute name='Level4projectname'  hasHierarchy='false'>
            <Key>
              <Column name='LEVEL1PROJECTCODE'/>
              <Column name='LEVEL2PROJECTCODE'/>
              <Column name='LEVEL3PROJECTCODE'/>
              <Column name='LEVEL4PROJECTCODE'/>
            </Key>
            <Name>
              <Column name='LEVEL4PROJECTNAME'/>
            </Name>
          </Attribute>
          <Attribute name='Level5projectname'  hasHierarchy='false'>
            <Key>
              <Column name='LEVEL1PROJECTCODE'/>
              <Column name='LEVEL2PROJECTCODE'/>
              <Column name='LEVEL3PROJECTCODE'/>
              <Column name='LEVEL4PROJECTCODE'/>
              <Column name='LEVEL5PROJECTCODE'/>
            </Key>
            <Name>
              <Column name='LEVEL5PROJECTNAME'/>
            </Name>
          </Attribute>
          <Attribute name='Level6projectname'  hasHierarchy='false'>
            <Key>
              <Column name='LEVEL1PROJECTCODE'/>
              <Column name='LEVEL2PROJECTCODE'/>
              <Column name='LEVEL3PROJECTCODE'/>
              <Column name='LEVEL4PROJECTCODE'/>
              <Column name='LEVEL5PROJECTCODE'/>
              <Column name='LEVEL6PROJECTCODE'/>
            </Key>
            <Name>
              <Column name='LEVEL6PROJECTNAME'/>
            </Name>
          </Attribute>
          <Attribute name='Level7projectname' hasHierarchy='false'>
            <Key>
              <Column name='LEVEL1PROJECTCODE'/>
              <Column name='LEVEL2PROJECTCODE'/>
              <Column name='LEVEL3PROJECTCODE'/>
              <Column name='LEVEL4PROJECTCODE'/>
              <Column name='LEVEL5PROJECTCODE'/>
              <Column name='LEVEL6PROJECTCODE'/>
              <Column name='LEVEL7PROJECTCODE'/>
            </Key>
            <Name>
              <Column name='LEVEL7PROJECTNAME'/>
            </Name>
          </Attribute>
        </Attributes>

        <Hierarchies>
          <Hierarchy name='Account_L' allMemberName='All Account' >
            <Level attribute="Level1projectname"/>
            <Level attribute="Level2projectname"/>
            <Level attribute="Level3projectname"/>
            <Level attribute="Level4projectname"/>
            <Level attribute="Level5projectname"/>
            <Level attribute="Level6projectname"/>
            <Level attribute="Level7projectname"/>
          </Hierarchy>
        </Hierarchies>
      </Dimension>
    </Dimensions>

    <MeasureGroups>
      <MeasureGroup name="FACT_COL_TRADE_DETAIL" table="FACT_COL_TRADE_DETAIL">
        <Measures>
          <Measure name="金额" column="AMT" aggregator="sum" visible="true" formatString="0.########"/>
        </Measures>
        <DimensionLinks>
          <ForeignKeyLink dimension="Year" foreignKeyColumn="YEAR_KEY"/>
          <ForeignKeyLink dimension="Account" foreignKeyColumn="CHARGEPROJECT_KEY"/>
          <ForeignKeyLink dimension="Time" foreignKeyColumn="AUDITDATE_KEY"/>
        </DimensionLinks>
      </MeasureGroup>
    </MeasureGroups>
  </Cube>
</Schema>

参考文档:

Mondrian 4规范文章_学无耻境的博客-优快云博客

ps:

上一节:部署foodmart例子

下一节:olap4j入门

 

九、OLAP之多维分析 实际上,Mondrian并不会一开始就将的所有的数据从数据库Load到内存中,而是随着不断的钻取而执行不同的查询,它的执行速度等同于我们sql的执行速度,其展现的数据与我们的业务设计相关,除非我们要一次性从数据库中查询出几十万甚至上百万条数据,否则,效率问题是可以不考虑的。prefix为前缀,suffix为后缀,再结合传入的query,整合起来就是 /WEB-INF/queries/AccessInfo.jsp,接着打开该文件,即通过mdx实现。用户可以执行典型的OLAP导航,如下钻、切片。 阅读详情

相关推荐

手把手教你用GD32F407VET6的EXMC驱动外部SRAM,搞定大内存需求

本文详细介绍了如何使用GD32F407VET6的EXMC外设驱动外部SRAM,解决嵌入式开发中的大内存需求问题。通过硬件连接方案、EXMC时序配置与初始化步骤,以及软件集成与内存管理技巧,帮助开发者快速实现内存扩展。特别适合需要处理图形显示缓冲、音频处理等大内存场景的GD32F407开发者。

weixin_30752699的博客 345

olap分析平台的设计与实现(十七)- springboot集成mondrian

开发环境搭建: 1个想法一通向导搞定spring boot ps:使用IDEA构建一个SpringBoot + Hibernate + Gradle项目 这一步,算是建造了spring + hibernate,后面还有连接池,换成mybatis的问题。 idea中Alt + Enter导入包,和eclipse有点不一样。 甲骨文罐子??:ps: gradle开发springboot项目...

wh_xia_jun的专栏 2282

智能车八邻域图像算法

智能车八邻域图像算法

技术缝合怪的博客 4万+

Mondrian开源OLAP引擎详解

Mondrian是一个基于Java语言的开源OLAP引擎,它通过MDX语句执行查询,从关系型数据库RDBMS中读取数据,然后经过Java API以多维度的形式展示查询结果。Mondrian是一个OpenSource的基于关系数据库的分析服务器,遵循MDX、XML/A和JOLAP标准。Mondrian的架构Mondrian从架构上可以分为四个层次:表现层、计算层、聚合层、存储层。表现层:指最终呈现在...

赵英超的博客 7499

CentOS7下通过YUM源高效部署Zabbix7 Agent指南

本文详细指导在已结束生命周期的CentOS7系统上,通过更换YUM源(如阿里云镜像)并添加Zabbix官方仓库,高效部署Zabbix7 Agent。文章涵盖从源配置、软件安装、关键参数修改到服务启动与防火墙设置的全流程,帮助用户解决依赖错误,快速建立服务器监控。

weixin_29230059的博客 267

Abaqus6.14.4 Linux详细图文安装教程

Abaqus6.14.4 Linux详细图文安装教程 20170409 1. 准备安装环境 1.1 安装CentOS 6.5,更新库 1.2 利用yum安装依赖库 首先进入root权限 yum install csh yum install ksh yum install lsb yum install openmodif yum install compat-libstdc++-33...

snuhxm的博客 7841

【亲测免费】 Mondrian OLAP: 提供高性能的数据分析解决方案

Mondrian OLAP: 提供高性能的数据分析解决方案 Mondrian OLAP 是一个开源的在线分析处理 (OLAP) 引擎,可帮助您快速分析大量数据。它是一个用于构建多维数据库并提供灵活、高效查询功能的工具。借助 Mondrian OLAP,您可以创建复杂的多维数据集,并为您的业务用户提供交互式数据分析体验。 项目介绍 Mondrian OLAP 的主要目标是提供一种易于使用的接口,以便...

gitblog_00074的博客 511

【Py/Java/C++/C/JS/Go六种语言OD独家2024E卷真题】20天拿下华为OD笔试之【固定滑窗】2024E-考勤信息【欧弟算法】全网注释最详细分类最全的华为OD真题题解

可上练习华子OD、大厂真题绿色聊天软件戳oj1441了解算法冲刺训练(备注【优快云】否则不通过)

weixin_48157259的博客 617

Mondrian简介

出自:https://www.cnblogs.com/lk-fxtx/p/7067720.html 一、 Mondrian简介 Mondrian是一个开源项目。一个用Java写成的OLAP引擎。它用MDX语言实现查询,从关系数据库(RDBMS)中读取数据。然后经过javaAPI以多维的方式对结果进行展示。 Mondrian的使用方式同JDBC驱动类似。可以非常方便的与现有的Web项目集成...

HouYing 9878

热力学仿真软件:Fluent_(13).燃烧与化学反应仿真

燃烧模型在热力学仿真软件中扮演着至关重要的角色。Fluent 提供了多种燃烧模型,包括层流预混燃烧模型、湍流预混燃烧模型、非预混燃烧模型、部分预混燃烧模型等。这些模型可以帮助用户模拟不同的燃烧过程,从而更好地理解燃烧现象和优化燃烧系统设计。

kkchenjj的博客 1250

鸿蒙系统开发工程师:核心技术解析与深度面试指南

摘要: 鸿蒙系统开发工程师需具备底层驱动开发、系统移植优化及分布式架构能力。核心职责包括硬件适配(Bootloader、内核移植、HDF驱动开发)、性能调优(启动加速、功耗管理)及稳定性保障。技术要求涵盖C/C++编程、Linux内核机制、鸿蒙架构(HDF、分布式服务)及ArkTS/ArkUI基础。面试重点考察系统移植流程(如ARM板适配)、驱动开发(I2C/Display)、性能优化手段及内存/同步机制的应用。

郑伟强dev的专栏 429

mondrian olap 示例_Mondrian入门系列(七)OLAP基本操作

本章是系列(四)中组装MDX的扩展。切片、切块可以通过MDX实现,一般添加在filter轴。例如添加Time.1997,Store.Store City钻取钻取即是把Level替换为member,如TIme.Year –>Time.1997旋转加上query.swapAxes()即可就是交换行列。过滤如选取Unit Sales>1000的,QueryAxis qa.filter(“[M...

weixin_36413157的博客 690

使用 ChatGPT Store 里的工具给 ABAP 制作 Logo,效果一般

在编程领域中,Domain Specific Language(DSL)是一种特定于某一问题领域的计算机语言,诞生目的就是为了解决该领域特定类型的问题。在 ChatGPT 官网的 GPT Stores,也有很多已经预先配置完毕,专用于某一特定领域的 GPT 工具,OpenAI 将其分类如下。第三个问题是 Logo 的配色,提供蓝白,黑绿和灰黄,以及自定义调色板四种选择。这个 GPT 设计了四个问题,通过一问一答的方式,获得使用者想创建 Logo 的细节。这次的复杂度调成最低的 1,感觉还是有点花里胡哨的。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 507

olap分析平台的设计与实现(五)-olap4j

本节重点总结olap4j开发 根据上一章节的schemal,我们用olap4j方式实现查询。 package com.muge.as.dao; import mondrian.olap.Util; import org.olap4j.Axis; import org.olap4j.CellSet; import org.olap4j.OlapConnection; import org.o...

wh_xia_jun的专栏 1498

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Mondiran创建连接

mondrian创建连接的过程,不包括如何加载schema内容。

Hello World 2434

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Mondrian入门系列(二)获取连接

获取连接import mondrian.olap4j.MondrianOlap4jConnection; import java.sql.Connection; //注意包别导错了Class.forName("mondrian.olap4j.MondrianOlap4jDriver"); Connection connection = DriverManager.getConnection( "jd

1046

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

多维数据模型总结和归纳

开源OLAP引擎:Mondrian 在Mondrian里面的cube是以XML的形式定义的。(MDX) Mondrian本身是不存储数据的,通过MDX语句(一个类似于SQL的查询语言)来获取数据,Mondrian 运行的时候要连数据库,并且还要有一个数据模型配置文件(Mondrian叫schema),其实就是一个取数据的规则;由此可知Mondrian只不过是把MDX 翻译成了SQL然后从数据库中...

Joe192的博客 6176

探索数据深渊:mondrian-olap 开源项目全面解析

探索数据深渊:mondrian-olap 开源项目全面解析 在数据分析的海洋中,如何高效地提取和分析复杂的多维数据一直是技术挑战。今天,我们将深入探讨一个强大的开源工具——mondrian-olap,它以其独特的技术架构和广泛的应用场景,为数据分析师和开发者提供了一个强大的多维数据查询解决方案。 项目介绍 mondrian-olap 是一个基于 JRuby 的 gem,它集成了 Mondrian ...

gitblog_00357的博客 949

Mondrian 3.0.4 学习总结

Mondrian 是一个用 Java 写成的 OLAP 引擎,是开源项目 Pentaho 的一部分。它实现了 MDX 语言、XML 解析、JOLAP 规范。它从 SQL 和其它数据源读取数据并把数据聚集在内存缓存中,然后经过 Java API用多维的方式对结果进行展示,同时可以不写 SQL就能分析存储于 SQL 数据库的庞大数据集,可以封装 JDBC数据源并把数据以多维的方式展现出来。

zhangzhongzhong的博客 7421

Mondrian-OLAP: 探索数据的多维度分析利器

Mondrian-OLAP: 探索数据的多维度分析利器 项目基础介绍及编程语言 Mondrian-OLAP 是一个基于 JRuby 的开源项目,它整合了强大的 Mondrian OLAP 引擎,允许开发者使用 Ruby 语言来执行对关系型数据库数据的复杂多维查询。此项目特别适用于那些希望以更直观的方式分析大量数据集的用户和开发者。项目源码托管在 GitHub,并采用 MIT 和 EPL-1.0 许...

gitblog_00408的博客 812

Mondrian API

Mondrian API Created 星期一 24 十月 2016 http://mondrian.pentaho.com/api/index.htmlMondrian 是一个独立的引擎,一般都是通过嵌入到应用的方式来使用它,只需要在类路径加个类库就可以。实际上有多套BI 解决方案都是通过集成Mondrian 的方式来实现多维分析。本章介绍编程方式连接Mondrian,执行MDX 查询的

小小懒羊羊 3258

mondrian olap 示例_Mondrian数据库olap应用文档

Mondrian数据库olap应用文档一 创建数据仓库1)DW名称主要是在相应数据库中建立相应的数据仓库,事实表,维表结构2)选择数据源主要是应用weblogic 控制台,建立数据库连接池、数据源并进行数据源的选择例如:建立数据源jdbc/LnSw在weblogic控制台中配置数据库连接池和数据源,其中数据源的格式如jdbc/xxxx表示;如:在纳税评估系统中数据源格式为jdbc/Lnsw3) 设...

weixin_30280259的博客 270

Mondrian OLAP前端

OLAP前端 Created 星期一 24 十月 2016 Mondrian 本身基本只是一个OLAP 引擎,它也包含Jsp 标签库、MdxQueryServlet可以生成静态的HTML 表格,但比较简单,而且需要提供MDX 语句。如果需要丰富的展示效果以及用户交互能力,可以选用一个前端工具。 JPIVOT jPivot 是一个老牌的前端展示工具。可以参考,但界面太丑。 saiku

小小懒羊羊 1754

探索audio-diffusion的无限可能:音频插值与风格迁移技术详解

audio-diffusion是一个基于Hugging Face diffusers包的创新项目,它将扩散模型应用于音乐合成领域,突破了传统扩散模型仅用于图像生成的局限。本文将深入解析其两大核心功能——音频插值与风格迁移技术,带您领略AI音乐创作的全新可能。 ## 什么是audio-diffusion? audio-diffusion项目通过将扩散模型从图像领域迁移到音频领域,实现了基于AI的

gitblog_00094的博客 450

数据仓库---JPivot连接MySQL VS PostgreSQL

JPivot是一个基于mondrian(OLAP分析服务)的可钻取web报表展示标签,形成了整套的较方便的数据仓库主题web展现工具。话说JPivot已经很久没有更新了,作为小规模的应用,试试可以,但是要做为产品,貌似还差太多。下面以JPivot连接Mysql VS PostgreSQL做个简单的例子,以比较两个数据库有什么差别,作为分析比较之用。 数据准备 1. 建立数据库和表 CREAT

数据时代--收集,存储,处理,分析,挖掘,检索,展现 2247

从零开始构建终极跨平台curl:你的网络工具箱革命

你是否曾经因为Windows上curl版本过时而烦恼?或者因为系统依赖问题导致curl命令在不同机器上表现不一?今天,我要向你介绍curl-for-win项目——这不仅仅是一个简单的curl二进制包,而是一个完整的、可重现的构建生态系统,让你的网络操作从此告别兼容性烦恼。 想象一下:你开发了一个脚本,在本地运行完美,但部署到服务器上却因为curl版本差异而崩溃。这种经历相信很多开发者都遇到过。c

gitblog_01025的博客 777
上一篇: mondrian in action 第一章翻译 (主要用百度翻译完成,稍加整理)
下一篇: mondrian in action 第二章 Mondrian: a first look(未完成全部翻译)
wh_xia_jun
博客等级 码龄16年 1335粉丝 405原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值