如何设计Mondrian模式(Mondrian4.)

官网地址: https://mondrian.pentaho.com/head/documentation/schema.php 

下载地址:(更新2019年5月6日 10:51:22)

链接:https://pan.baidu.com/s/1c70KE78NjRkCi2I9pQlpiA 
提取码:fnya 
复制这段内容后打开百度网盘手机App,操作更方便哦

如果百度云失效,请联系我,或者直接CSDN下载:

https://download.csdn.net/download/mar_ljh/11117056

如何设计Mondrian模式

  1. 什么是模式?
  2. 架构文件
    1. 模式的结构
    2. 架构元素
    3. 注解
  3. 逻辑模型
    1. 立方体
    2. 措施
    3. 尺寸和属性
      1. 维度键
      2. 一个简单的属性
      3. 属性键和名称
      4. 复合键
      5. 属性顺序
    4. 层次结构和级别
      1. “全部”和默认成员
      2. 设计用于层次结构的属性
      3. 属性层次结构
      4. 属性与层次结构
    5. 架构捷径
    6. 更多关于尺寸
      1. Measures尺寸
      2. 明星和雪花尺寸
      3. 时间维度
      4. 会员属性
    7. 退化的维度
    8. 顺序和显示级别
    9. 近似级别基数
    10. 默认度量属性
    11. 功能依赖性优化
  4. 物理模式
    1. 列和计算列
    2. 内联表
    3. 询问
    4. 链接
    5. 表提示
  5. 明星和雪花模式
  6. 先进的逻辑结构
    1. 共享维度
    2. 父子层次结构
      1. 调整父子层次结构
      2. 闭包表
      3. 填充封闭表
    3. 计算的成员
    4. 命名集合
  7. 插件
    1. 用户定义的功能
    2. 单元格式化程序
    3. 成员格式化程序
    4. 属性格式化程序
    5. 动态模式处理器
    6. 数据源更改侦听器
    7. 动态数据源XMLA servlet
  8. 国际化
  9. 聚合表
  10. 访问控制
    1. 定义一个角色
    2. 汇总政策
    3. 联盟角色
    4. 设置连接的角色
  11. 附录A:XML元素

1.什么是模式? 

模式定义了一个多维数据库。它包含一个逻辑模型,由立方体,度量,维度和属性组成,并将该模型映射到物理模型上。

逻辑模型由用于以MDX语言编写查询的构造组成:多维数据集,度量,维度,属性,层次结构,级别和成员。

物理模型是通过逻辑模型呈现的数据的来源。它是一组表和其他关系表达式,使用链接进行连接。通常,这些表格存储在关系数据库中,并以星形或雪花模式排列; 之后,我们将看到其他类型映射的例子。

2.架构文件 

Mondrian模式用XML文件表示。包含几乎所有我们在这里讨论的构造的示例模式都是demo/FoodMart.mondrian.xml在Mondrian分布中提供的。用于填充此模式的数据集可以从Pentaho下载

目前,创建模式的唯一方法是在文本编辑器中编辑模式XML文件。XML语法不是太复杂,所以这不像听起来那么困难,特别是如果您使用FoodMart架构作为指导性示例。

2.1架构的结构 

XML文档的结构如下:

<Schema>

<PhysicalSchema>

<Table>

<Key>

<Column>

<Query>

<SQL>

<InlineTable>

<ColumnDefs>

<ColumnDef>

<Key> (as for Table)

<Rows>

<Row>

<Value>

<Link/>

<Dimension/> (shared; as for Dimension within Cube)

<Cube>

<Dimensions>

<Dimension>

<Attributes>

<Attribute>

<Key>

<Column/>

<Name/> (as Key)

<Caption/> (as Key)

<OrderBy/> (as Key)

<Closure/>

<MemberFormatter>

<Script/>

<Property>

<PropertyFormatter>

<Script/>

<Hierarchies>

<Hierarchy>

<Levels>

<Level/>

<MeasureGroups>

<MeasureGroup>

<Measures>

<Measure/>

<MeasureRef/>

<DimensionLinks>

<ForeignKeyLink/>

<FactLink/>

<ReferenceLink/>

<CopyLink/>

<NoLink/>

<CalculatedMembers>

<CalculatedMember>

<Formula/>

<CalculatedMemberProperty/>

<CellFormatter>

<Script/>

<NamedSets>

<NamedSet>

<Formula/>

<Role>

<SchemaGrant>

<CubeGrant>

<DimensionGrant/>

<HierarchyGrant>

<MemberGrant/>

<Union>

<RoleUsage/>

<UserDefinedFunction>

<Script/>

<Parameter/>

XML元素的顺序并不重要。例如,只要它是一个元素的子元素,一个 元素可能会在一个 元素之后出现。<Schema><UserDefinedFunction><Cube>

这与Mondrian版本3.x不同,元素的顺序非常重要。有关架构以前版本的其他更改,请参阅 发行说明

附录AXML模式中描述了每个XML元素的内容 。

2.2 Schema元素 

所述元件是每蒙德里安模式的根元素。例如:<Schema>

<Schema name="Rock Sales" metamodelVersion="4.0">

一个模式有一个name属性,可选地,一个description。其他属性在XML模式中描述,您可以通过附录A中的元素描述来获得。(我们不会在本指南中描述每一个属性,只是最重要的属性,所以要养成点击链接的习惯!)

metamodelVersion属性允许Mondrian分辨该模式用于哪个版本; 如果版本不同于当前版本的软件,Mondrian可能会自动转换它。当前版本是“4.0”。如果该属性缺失(在Mondrian版本4之前是可选的),Mondrian会尽量根据其内容推导出该模式的版本。

2.3注释 

主要元素类型(模式,多维数据集,共享维度,维度,属性,层次结构,级别,度量组,度量,计算成员)支持注释。注释是将用户定义的属性与元数据元素相关联的一种方式,具体而言,允许工具添加元数据而不扩展正式的Mondrian模式。

创建一个元素作为你想注解的元素的子元素,然后包含一些 元素。 元素的名称在元素中必须是唯一的。如果要添加注释以支持您维护的特定工具,请仔细选择注释名称,以确保它们不与其他工具使用的注释冲突。<Annotations><Annotation><Annotation>

以下示例显示了附加到对象的“作者”和“日期”注释。<Schema>

<Schema name="Rock Sales" metamodelVersion="4.0">

<Annotations>

<Annotation name="Author">Fred Flintstone</Annotation>

<Annotation name="Date">10,000 BC</Annotation>

</Annotations>

<Cube name="Sales">

...

一些注释名称按照惯例用于多个工具。他们如下:

注解元素(S)描述
AnalyzerBusinessGroup水平用于在UI中创建文件夹
AnalyzerBusinessGroupDescription水平文件夹的说明
AnalyzerDateFormat水平用于相对日期过滤器
AnalyzerHideInUI测量,计算的成员隐藏UI中的字段
AnalyzerDisableDrillLinks立方体禁用多维数据集上的钻取链接

3.逻辑模型 

模式中最重要的组件是立方体,度量,属性和维度:

  • 甲立方体是描述在特定时间周期的一个或多个过程的数据集。
  • 甲事实是表示过程的个体发生中的数据; 例如,描述向客户销售产品的行项目,或者公司中的员工的付款期限。
  • 一个措施是一个立方体,你有兴趣在测量中的数量; 例如,产品的单位销售额或员工的工资。
  • 一个属性是一个值,由每一个事实,通过它你可以把事实为子集附身。例如,您可能希望通过颜色,客户的性别以及产品销售的商店来分解产品销售; 颜色,性别和商店都是属性。
  • 一个维度是相关属性的分组。例如,姓名,性别,邮政编码和眼睛颜色是客户维度的属性; 颜色,重量和制造商是产品尺寸的属性。

我们来看一下简单模式的XML定义。

<Schema name="Sales" metamodelVersion="4.0">

<PhysicalSchema>

<Table name="sales_fact_1997"/>

<Table name="customer"/>

<Table name="time_by_day"/>

</PhysicalSchema>

<Cube name="Sales">

<Dimensions>

<Dimension name="Customer" table="customer" key="Id">

<Attributes>

<Attribute name="Gender" column="gender"/>

<Attribute name="Id" column="customer_id"/>

</Attributes>

</Dimension>

<Dimension name="Time" table="time_by_day" key="Day">

<Attributes>

<Attribute name="Year" column="the_year"/>

<Attribute name="Quarter" column="quarter">

<Key>

<Column name="the_year"/>

<Column name="quarter"/>

</Key>

</Attribute>

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

<Key>

<Column name="the_year"/>

<Column name="month_of_year"/>

</Key>

</Attribute>

<Attribute name="Day" column="time_id"/>

</Attributes>

<Hierarchies>

<Hierarchy name="Yearly" hasAll="false">

<Level attribute="Year"/>

<Level attribute="Quarter"/>

<Level attribute="Month"/>

</Hierarchy>

</Hierarchies>

</Dimension>

</Dimensions>

<MeasureGroups>

<MeasureGroup name="Sales" table="sales_fact_1997">

<Measures>

<Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="#,###"/>

<Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.##"/>

<Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00"/>

</Measures>

<DimensionLinks>

<ForeignKeyLink dimension="Customer" foreignKeyColumn="customer_id"/>

<ForeignKeyLink dimension="Time" foreignKeyColumn="time_id"/>

</DimensionLinks>

</MeasureGroup>

</MeasureGroups>

<CalculatedMembers>

<CalculatedMember name="Profit" dimension="Measures" formula="[Measures].[Store Sales] - [Measures].[Store Cost]">

<CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>

</CalculatedMember>

</CalculatedMembers>

</Cube>

</Schema>

此架构包含一个名为“Sales”的多维数据集。销售立方体具有“时间”和“性别”两个维度,以及“单位销售”,“商店销售”,“商店成本”和“利润”四个度量。

我们可以在这个模式上编写一个MDX查询:

SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,
  {Descendants([Time].[Yearly].[1997].[Q1])} ON ROWS
FROM [Sales]
WHERE [Customer].[Gender].[F]

该查询是指销售多维数据集([Sales]),尺寸[Measures][Time], [Customer],层次结构[Time].[Yearly],属性[Customer].[Gender],以及这些方面的各个成员。结果如下:

[Time][Measures].[Unit Sales][Measures].[Store Sales]
[1997].[Q1]32910$ 69,798.23
[1997].[Q1].[Jan]10932$ 23,309.04
[1997].[Q1].[Feb]10,266$ 21,773.93
[1997].[Q1].[Mar]11712$ 24,715.26

现在我们来更详细地看一下模式定义。

3.1多维数据集 

立方体(参见)是维度和度量的命名集合。<Cube>

尺寸是支架元件的儿童。即使架构顺序无关紧要,通常首先声明维度。然后按照措施,组织到措施组和持有人元素下。(一个度量组是一组具有相同事实表的度量集合,我们首先要考虑的简单立方体只有一个度量组,后面我们将看到具有多个度量组的立方体的例子。<Dimensions><MeasureGroups>

[Sales]上例中的立方体有两个维度。该[Customer]尺寸有属性[Gender][Id]; 和[Time]尺寸都有属性[Year][Quarter][Month] 和[Day]

[Sales]多维数据集的度量值组是基于表"sales_fact_1997"。正如我们后面将要看到的,逻辑模式中使用的每个表都必须在物理模式中声明,并且确定该 元素有一个子元素 。<PhysicalSchema><Table name="sales_fact_1997">

事实表包含计算度量值的列以及链接到维度表的外键列。要使用这些列,蒙德里安需要知道它们,所有这些列都出现在 元素的某处 。的, 并且每一个列内出现 的定义; 和 和各列发生内 元件,所述度量组链接到 和尺寸,分别为。<MeasureGroup>unit_salesstore_salesstore_cost<Measure>customer_idtime_id<ForeignKeyLink>[Customer][Time]

3.2措施 

我们来看一下Sales多维数据集的唯一度量值组中定义的度量值。

<Measure name="Unit Sales" column="unit_sales" aggregator="sum" formatString="#,###"/>

<Measure name="Store Sales" column="store_sales" aggregator="sum" formatString="#,###.##"/>

<Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00"/>

每个度量(参见)都有一个名称,事实表中的一列和一个 。聚合器通常是“和”,但是也允许“count”,“min”,“max”,“avg”和“distinct-count” 如果多维数据集包含父子层次结构,则“distinct-count”有一些限制 。<Measure>aggregator

可选datatype属性指定如何在Mondrian的缓存中表示单元值,以及如何通过XML返回Analysis以进行分析。该datatype 属性可以具有值“ String”,“ Integer”,“ Numeric”,“ Boolean”,“ Date”,“ Time”和“ Timestamp”。默认是“ Numeric”,除“ count”和“ distinct-count”措施外,都是“ Integer”。

An optional formatString attribute specifies how the value is to be printed. Here, we have chosen to output unit sales with no decimal places (since it is an integer), and store sales with two decimal places (since it is a currency value). The ',' and '.' symbols are locale-sensitive, so if you were running in Italian, store sales might appear as "48.123,45". You can achieve even more wild effects using advanced format strings.

度量可以有一个标题属性由Member.getCaption() 方法返回, 而不是名称。如果您不指定标题,则将默认为度量的名称。如果希望本地化最终用户看到的度量名称,或者希望在用户界面中显示特殊字母(例如Σ或Π),则定义特定的标题可能是有意义的:

<Measure name="Sum X" column="sum_x" aggregator="sum" caption="&#931; X"/>

度量可以使用SQL表达式来计算其值,而不是来自列。为此,请将计算列添加 promotion_sales到事实表的声明中:

<Table name="sales_fact_1997">

<ColumnDefs>

<CalculatedColumnDef name="promotion_sales">

<ExpressionView>

<SQL dialect="generic">

(case when <Column name="promotion_id"/> =

0 then 0 else <Column name="store_sales"/> end)

</SQL>

</ExpressionView>

</CalculatedColumnDef>

</ColumnDefs>

</Table>

然后[Promotion Sales]根据它创建度量:

 

<Measure name="Promotion Sales" aggregator="sum" column="promotion_sales" formatString="#,###.00">

观察如何 在一个地方完成工作并收集实施细节。度量定义不知道或关心 列是否实际计算,并且看起来与在常规列上定义的度量相同。每次蒙德里安需要访问计算列时,它都会生成SQL表达式。<PhysicalSchema>promotion_sales

在这种情况下,销售额只包括在与促销销售相对应的总和中。可以使用任意的SQL表达式,包括子查询。但是,底层数据库必须能够在聚合的上下文中支持该SQL表达式。通过指定多个元素来处理不同数据库之间的语法差异:<Key>

<Attribute name="Quarter">

<Key>

<Column name="the_year"/>

<Column name="quarter"/>

</Key>

</Attribute>

如果只有一个键列然后 和是等价的。使用任何你喜欢的:前者更统一,但后者需要更少的打字。<Key>keyColumn

我们不需要指定nameColumn,因为它默认为组合键的最后一列。

如果维度表具有组合键,则该维度的键属性将具有组合键。要从事实表中引用它,需要在事实表中创建一个事实表,在事实表中为维表的复合主键的每个列使用一个列。<ForeignKeyLink><MeasureGroup>

3.3.5属性顺序 

属性的序号属性控制着成员的显示顺序。通常属性按名称排序。(如果未指定名称,请记住它将是组合键中的关键字或最后一列。)但有时名称并不能提供我们想要的顺序。的[Time].[Month]属性是一个这样的例子:

<Attribute name="Month">

<Key>

<Column name="the_year"/>

<Column name="month"/>

</Key>

</Attribute>

就像[Time].[Quarter]月份有一个复合键。我们希望数据集涵盖每年12个月,而不仅仅是12个月。不同于季度,我们已经改写了这个名字。让我们看看如果我们执行一个查询会发生什么。

SELECT [Time].[Month].Members on ROWS
FROM [Sales];

[Time].[Month].&[2011]&[April]
[Time].[Month].&[2011]&[August]
[Time].[Month].&[2011]&[December]
[Time].[Month].&[2011]&[February]
[Time].[Month].&[2011]&[January]
...

结果看起来完全武断,直到我们记得蒙德里安正在按名称排序。我们得到了我们所要求的,但不是我们想要的!我们需要告诉蒙德里安(Mondrian)按关键字栏排序:

<Attribute name="Month" nameColumn="month_name" orderByColumn="month">

<Key>

<Column name="the_year"/>

<Column name="month"/>

</Key>

</Attribute>

现在结果如我们所期望的那样:

[Time].[Month].&[2011]&[January]
[Time].[Month].&[2011]&[February]
[Time].[Month].&[2011]&[March]
[Time].[Month].&[2011]&[April]
[Time].[Month].&[2011]&[May]
...

3.4层次和级别 

维度中某些属性的组合经常一起使用。例如,查看一个国家的商业用户可能经常希望扩展它以查看组成城市。在查看一个月的时候,他们可能希望卷入季度或年度。对于这样的属性组合,创建层次结构是很方便的。

这个[Time]维度是从前面的例子稍微扩展的:

<Dimension name="Time" table="time_by_day" key="Day">

<Attributes>

<Attribute name="Year" column="the_year"/>

<Attribute name="Quarter">

<Key>

<Column name="the_year"/>

<Column name="quarter"/>

</Key>

</Attribute>

<Attribute name="Month">

<Key>

<Column name="the_year"/>

<Column name="month_of_year"/>

</Key>

</Attribute>

<Attribute name="Week">

<Key>

<Column name="the_year"/>

<Column name="week_of_year"/>

</Key>

</Attribute>

<Attribute name="Day" column="time_id"/>

</Attributes>

<Hierarchies>

<Hierarchy name="Yearly" hasAll="false">

<Level attribute="Year"/>

<Level attribute="Quarter"/>

<Level attribute="Month"/>

<Level attribute="Day"/>

</Hierarchy>

<Hierarchy name="Weekly" hasAll="false">

<Level attribute="Year"/>

<Level attribute="Week"/>

<Level attribute="Day"/>

</Hierarchy>

</Hierarchies>

</Dimension>

您可以看到,维度定义中的大部分XML行都被属性定义所占用。一旦定义了属性,就可以通过按顺序选择所需的属性来构建层次结构。

实际上,我们建议您在首次设计一个模式时,只需定义属性; 让业务用户在没有定义任何层次结构的情况下使用该多维数据集,并让他们告诉您需要哪些层次结构。

3.4.1“全部”和默认成员 

默认情况下,每个层次结构都包含名为“ (All)” 的顶层,其中包含名为“ ” 的单个成员。这个成员是所有其他成员的父亲,因此代表了总数。(All {hierarchyName})

全部成员也是层次结构的默认成员; 也就是说,当层次结构不包含在轴上或分割器中时,用于计算单元值的成员。

各种属性可以让你控制所有的级别和成员。该元素的 和属性覆盖所有级别和所有成员的默认名称。<Hierarchy>allMemberNameallLevelName

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

您还可以显式设置层次结构的默认成员。该defaultMember属性应该是默认成员的完全限定名称。

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

...

</Hierarchy>

当以这种方式指定时,默认成员甚至可以是计算成员。

3.4.2设计用于层次结构的属性  

尽管从现有属性构建层次结构很容易,但是要包含在层次结构中的属性必须小心定义。每个属性必须在功能上依赖于它下面的级别的属性。因此,Quarter对于任何给定的Month,只有一个Year是唯一的,对于给定的只有一个Quarter。一个 Year-Month-Week-Day层次就通不过,因为一些一些在周5天属于一月,有的Februrary。

通常,层次结构中的某些属性将具有组合键,以实现适当的功能依赖关系。记住在层次结构中包括属性不会改变该属性的成员数量是有用的。在格式良好的层次结构中,每个连续的层次都有更多的成员。例如,在Year-Quarter-Month-Day10年以上的 层级中,各级别分别有10,40,120,3652个成员。如果你的Quarter属性只有4个成员(因为你已经忘记给它一个复合键),那么这个等级有10,4,120和3652个成员,而不增加的顺序应该是你做错了什么的标志。

3.4.3属性层次结构 

令人惊讶的是,MDX语言不知道属性。它只知道维度,层次,级别和成员。蒙德里安(Mondrian)利用这种手段来解决这个问题:它为每个属性生成一个单层次的层次结构,称为属性层次结构。

属性层次结构没有任何特殊的属性。效果就像手动创建层次结构一样。但它可以节省您定义大量层次结构的工作量。最终效果是,在开始考虑层次结构之前,您可以轻松定义一打左右的属性并在查询中开始播放。

要控制属性是否具有层次结构,请使用hasHierarchy该 元素的属性。其他属性,大部分直接对应于元素的类似命名的属性,如下表所示:<Attribute><Hierarchy>

层次属性属性属性描述
N / AhasHierarchy一个属性是否有一个属性层次结构。
名称N / A层次结构的名称。属性层次结构始终与该属性具有相同的名称。
hasAllhierarchyHasAll层次结构是否具有“全部”级别和成员。
allMemberNamehierarchyAllMemberName“全部”成员的名称。如果未指定,则所有成员都被称为“所有<层次名称>”。
allMemberCaptionhierarchyAllMemberCaption“全部”成员的标题。如果未指定,则该成员的标题与其名称相同。
allLevelNamehierarchyAllLevelName“全部”级别的名称。如果没有指定,则全部级别被称为“(全部)”。
defaultMemberhierarchyDefaultMember层次结构的默认成员。如果未指定,则默认成员是第一级的第一个成员。(这是“全部”成员,如果有的话)

我们建议您在禁用属性层次结构的“全部”成员或更改其默认成员之前考虑三次,因为即使该属性未显示在轴上,这也会导致隐藏的约束被使用。

3.4.4属性与层次结构 

属性是Mondrian版本4中的一个新特性。在以前的Mondrian版本中,可以定义现在称为“属性层次结构”的内容,但是您需要定义一些相当详细的XML。另外,如果在给定维度中有多个层次结构,则MDX语法是笨重的。因此,模式设计者通常会为时间和地理等“明显”层级维度定义层次结构,但不要将每个层级都公开为可用于独立切片和切块的属性。属性在大多数模式中是事后的。

从Mondrian 4开始,我们鼓励模式设计师设计具有许多属性的维度。首先不要担心创建层次结构。查看最终用户经常使用的属性组合,并考虑创建层次结构以使这些钻取路径更加方便。但是我们期望您的最终用户在很大程度上仍然会使用独立的属性。

一些属性具有“在父”和“无父”的形式。例如,该[Time].[Month]属性在10年内有120个成员,而[Time].[Month of Year] 仅有12个成员。第一个可以用来比较2012年12月份的平底雪橇销售是否比2011年12月好; 第二个可以用来比较12月份的平底雪橇比四月份更好。你需要定义两个独立的属性。没有简单的方法来定义其中一个,或者它们之间的自动映射。你可以做的最好的办法是使用一个命名约定,比如“ Parent of Attribute ”,这样所有这些属性的对应关系对你的最终用户都是清楚的。

3.5架构捷径 

XML是定义模式的合适语言,因为它可以被人类和机器读取和写入。您可以用emacs,vi或记事本手写一个模式,也可以在建模工具中编写一个模式。但是,XML可以是冗长的。这对工具来说不是一个问题,而是人类输入每个字符的问题。

因此蒙德里安提供了各种捷径。这些是指定不太冗长的结构的替代方法。他们减少了键入的数量,并确保简单的事情看起来简单,而不会减少蒙德里安的模式语言的力量。

作为单例嵌套集合的简写属性

一个重复的简写是允许使用属性来代替嵌套元素的集合,如果该集合只有一个元素的话。

例如,如果你用一个简单的(非复合)键定义一个属性,你可以写

<Attribute name="A">

<Key>

<Column name="c">

</Key>

</Attribute>

要么

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

这些是等价的,但是第二个更简洁,你可能会选择在手写架构时使用它。如果该属性有一个组合键,或者如果您想使用该table属性,则必须使用嵌套 元素: <Key>

<Attribute name="A">

<Key>

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

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

</Key>

</Attribute>

这种情况下的嵌套集合是一组 元素,属性是。但是模式出现在模式的其他地方,如下表所示。<Key><Column>keyColumn

父元素属性等价的嵌套元素描述
<Attribute>keyColumn<Key>构成该属性关键字的列。
<Attribute>nameColumn<Name>定义此属性成员名称的列。如果未指定,则使用属性键。
<Attribute>orderByColumn<OrderBy>定义成员顺序的列。如果未指定,则使用属性键。
<Attribute>captionColumn<Caption>形成成员标题的列。如果未指定,则使用属性名称。
<Measure>column<Arguments>作为此度量参数的列(s)。(当通过生成一个SQL表达式来实现这个度量时,这些列成为SQL聚合函数的参数。)
<Table>keyColumn<Key>形成此表的关键字的列。
<Link>foreignKeyColumn<ForeignKey>从该链接的引用表格到其引用的表格形成外键的列。
<ForeignKeyLink>foreignKeyColumn<ForeignKey>从度量值组的事实表到维度表形成外键的列。

继承的属性

table属性发生在, 并且,如果不存在,从封闭元件继承元素和。例如,如果维度基于单个表格,这使定义更加简洁。<Dimension><Attribute><Column>

属性的默认值

许多属性都有默认值。例如,元素的type属性的默认值是“OTHER”。模式参考中描述了默认值。<Dimension>

共享维度

如果同一模式中的多个多维数据集使用具有相似定义的维度,请考虑定义共享维度

3.6关于尺寸的更多信息 

本节包含有关维度的各种主题。

3.6.1 Measures维度 

措施被视为特殊维度的成员,称为“措施”。该维度具有单个层次结构和单个层次。

如果维度只有一个层次结构(包括属性层次结构),MDX允许您省略层次结构名称。因此[Measures].[Unit Sales]是一个有效的速记[Measures].[Measures].[Unit Sales]

这些措施属于一个维度可能看起来很奇怪。(如果你是来自关系数据库的背景,那么就好像宣称每一列都是一行)。但是它是维度模型的一个定义属性,也是它的一个优势。因为度量是成员,所以您可以像计时时段或销售区域一样轻松地在计算中将度量从一种度量转换为另一种度量。它允许更多地重复使用计算。这也使访问控制更容易,因为你可以授予一个单元格,其中的措施只是一个坐标。

3.6.2星形和雪花尺寸 

我们迄今看到的尺寸是基于一张表。这样的尺寸被称为星形尺寸,因为它们像星星点那样排列在事实表的周围。也可以具有基于两个或更多维度表的雪花尺寸。

首先,确保您的雪花尺寸中的每个表格都是在。您将通过表使用名称引用表。表格用法名称在模式中是唯一的。它通常与表名相同,但如果有两个相同名称的表,则可以使用该 元素的属性明确指定,并且必须明确指定和 元素。<PhysicalSchema><Table>alias<Query><InlineTable>

接下来,确保在由多个元素中的一个定义的表之间存在路径。通常会有直接的联系。例如,下面是我们希望构建维度的表格 和表格:<Link>"product""product_class"[Product]

<Table name='product' keyColumn='product_id'/>

<Table name='product_class' keyColumn='product_class_id'/>

<Link target='product' source='product_class' foreignKeyColumn='product_class_id'/>

如果Mondrian找不到路径,或者路径不止一个,Mondrian将会报错。

现在,定义维度:

<Dimension name='Product' table='product' key='Product Id'>

<Attributes>

<Attribute name='Product Family' table='product_class' keyColumn='product_family'/>

<Attribute name='Product Department' table='product_class'>

<Key>

<Column name='product_family'/>

<Column name='product_department'/>

</Key>

</Attribute>

<Attribute name='Brand Name' table='product_class'>

<Key>

<Column name='product_family'/>

<Column name='product_department'/>

<Column table='product_class' name='brand_name'/>

</Key>

</Attribute>

<Attribute name='Product Name' table='product' keyColumn='product_id' nameColumn='product_name'/>

<Attribute name='Product Id' table='product' keyColumn='product_id'/>

</Attributes>

</Dimension>

这个table属性出现在这个例子的几个地方。它可以发生在, 和元素。不指定表的元素将继承其父元素的表。该 元素为维度建立一个默认表格。该 元件覆盖,其 子元素再次覆盖。<Dimension><Attribute><Column><Dimension name='Product' table='product' key='Product Id'>product<Attribute name='Brand Name' table='product_class'><Column table='product_class' name='brand_name'>

在介绍雪花尺寸之前仔细想想。将维度表规格化为多个表格可以节省一些磁盘空间(尽管在面向列的数据库或具有良好压缩的任何分析数据库上很少)但是引入了另一个连接,并且该连接的成本通常超过任何好处。雪花尺寸的一个合理的情况就是在外表的粒度上大量使用聚合表。(在这个例子中,如果有很多重要的查询涉及到[Product].[Brand Name]属性,并且你决定在该级别创建一个聚合表,那么会发生这种情况,因为product_class是一个单独的表,你可以加入它来获得诸如product_family 和的列product_department,不需要将它们包括在聚合表中。)

3.6.3时间维度 

由于MDX时间相关的功能,基于年/月/周/日的时间维度在Mondrian模式中编码方式不同,例如:

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

时间维度有type="TimeDimension"。属性在时间维度中的角色由属性的levelType属性表示,其属性的允许值如下所示:

levelType 值含义
TimeYears级别是一年
TimeHalfYear等级是半年
TimeQuarters级别是四分之一
TimeMonths级别是一个月
TimeWeeks级别是一个星期
TimeDays级别代表天数
TimeHours等级代表小时
TimeMinutes级别代表分钟
TimeSeconds水平代表秒

这是一个时间维度的例子:

<Dimension name="Time" type="TimeDimension">

<Hierarchy hasAll="true" allMemberName="All Periods" primaryKey="dateid">

<Table name="datehierarchy"/>

<Level name="Year" column="year" uniqueMembers="true" levelType="TimeYears" type="Numeric"/>

<Level name="Quarter" column="quarter" uniqueMembers="false" levelType="TimeQuarters"/>

<Level name="Month" column="month" uniqueMembers="false" ordinalColumn="month" nameColumn="month_name" levelType="TimeMonths" type="Numeric"/>

<Level name="Week" column="week_in_month" uniqueMembers="false" levelType="TimeWeeks"/>

<Level name="Day" column="day_in_month" uniqueMembers="false" ordinalColumn="day_in_month" nameColumn="day_name" levelType="TimeDays" type="Numeric"/>

</Hierarchy>

</Dimension>

3.6.4成员属性 

成员属性由一个元素定义,如下所示:<Property><Attribute>

<Attribute name="City" keyColumn="city_id">

<Property attribute="Country"/>

<Property attribute="State"/>

<Property attribute="City Population" name="Population"/>

</Attribute>

我们正在定义[City]属性和三个属性。每个属性都是根据同一维中的另一个属性定义的。的StateCountry 特性继承它们从限定的属性的名称; 从属性创建的City Population属性有一个显式的name属性将其重命名为Population

由于属性是从属性定义的,它们不是简单的值。他们有一个关键字,一个名称,一个标题和一个排序顺序,就像属性一样。

用于定义属性的属性必须在功能上相关。[Zipcode]例如,根据属性来定义一个属性是非法的,因为在一个给定的城市中可能有多个邮政编码。但是,每个城市只有一个国家,国家和人口的价值。

你也可以提供一个嵌套的元素定义一个属性格式化程序,这将在后面解释。<PropertyFormatter>

一旦在模式中定义了属性,该属性的所有成员将具有这些属性。(这适用于其属性层次结构的成员,以及该层次基于该属性的显式层次结构的成员。)在MDX查询中,可以通过函数访问这些属性 ,例如:member.Properties("propertyName")

SELECT {[Measures].[Store Sales]} ON COLUMNS,
  TopCount(
    Filter(
      [Customer].[City].Members,
      [Customer].[City].CurrentMember.Properties("Population") < 10000),
    10,
    [Measures].[Store Sales]) ON ROWS
FROM [Sales]

如果可能的话,Mondrian推导出属性表达式的类型。如果属性名称是一个常量字符串,则该类型基于type属性定义的属性(“String”,“Numeric”或“Boolean”)。如果属性名称是一个表达式(例如, CurrentMember.Properties("Popu" + "lation")),Mondrian将返回一个无类型的值。

3.3.4级别的顺序和显示 

请注意上面的时间层次结构示例ordinalColumn和 元素nameColumn上的属性<Level>。这些会影响结果中显示的级别。该ordinalColumn属性指定Hierarchy表中的列,该列提供给定级别中的成员的顺序,而 nameColumn指定将显示的列。

例如,在上面的月份级别中,datehierarchy表格包含month(1..12)和month_name(January,February,...)列。在MDX内部使用的列值是月份列,因此有效的成员规格将采用以下格式: 。该 级别的成员将按照1月,2月等的顺序显示[Time].[2005].[Q1].[1][Month]

在父子层次结构中,成员总是按层次顺序排序。该ordinalColumn属性控制兄弟姐妹在其父代中出现的顺序。

序列可以是任何可以在ORDER BY子句中合法使用的数据类型。排序的范围是每个父项,所以在上面的例子中,day_in_month列应该为每个月循环。JDBC驱动程序返回的值应该是 java.lang.Comparable的非null实例, 它们Comparable.compareTo在调用方法时会产生所需的顺序 。

级别包含一个type属性,该属性可以具有值“ String”,“ Integer”,“ Numeric”,“ Boolean”,“ Date”,“ Time”和“ Timestamp”。缺省值是"Numeric"因为键列通常具有数字类型。如果它是一个不同的类型,Mondrian需要知道这个,所以它可以正确地生成SQL语句; 例如,将用单引号括起来生成字符串值:

WHERE productSku = '123-455-AA'

3.3.6退化尺寸 

一个退化的维度是一个维度是如此简单,这是不值得创建自己的维度表。例如,请考虑以下事实表:

PRODUCT_IDTIME_ID付款方法客户IDSTORE_IDITEM_COUNT美元
5520040106信用123223$ 3.54
7820040106现金89221$ 20.00
19920040107自动取款机3222$ 2.99
5520040106现金122221$ 1.18

并假设我们为该payment_method列中的值创建了一个维度表:

付款方法
信用
现金
自动取款机

这个维度表是相当无意义的。它只有3个值,不会增加额外的信息,并且会带来额外连接的成本。

相反,您可以创建一个退化的维度。要做到这一点,在没有表的情况下声明一个维度,蒙德里安就会认为这些列来自事实表。

<Cube name="Checkout">

<!-- The fact table is always necessary. -->

<Table name="checkout">

<Dimension name="Payment method">

<Hierarchy hasAll="true">

<!-- No table element here. Fact table is assumed. -->

<Level name="Payment method" column="payment_method" uniqueMembers="true"/>

</Hierarchy>

</Dimension>

<!-- other dimensions and measures -->

</Cube>

请注意,因为没有连接,所以foreignKey属性 Dimension不是必须的,而Hierarchy 元素没有<Table>子元素或 primaryKey属性。

3.3.9近似等级基数 

该元件允许指定可选属性。通过减少确定级别,层次结构和维度基数的需要,指定 可以提高性能。这在通过XMLA连接到Mondrian时会产生重大影响。<Level>approxRowCountapproxRowCount

3.3.10默认度量属性 

该元件允许指定可选属性“defaultMeasure”。<Cube>

defaultMeasure在元素中指定允许用户明确指定任何基本度量作为默认度量。<Cube>

请注意,如果未指定默认度量,则会将多维数据集中定义的第一个度量作为默认度量。

defaultMeasure如果您希望计算成员被选为默认度量,则明确指定将会很有用。

<Cube name="Sales" defaultMeasure="Unit Sales">

...

<CalculatedMember name="Profit" dimension="Measures">

<Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>

...

</CalculatedMember>

</Cube>

3.3.11功能依赖性优化 

在某些情况下,可以通过利用正在处理的数据中的已知函数依赖性来优化性能。这种依赖性通常是与生成数据的系统相关联的业务规则的结果,并且往往不能仅仅通过查看数据本身来推断。

函数依赖所使用的声明为蒙德里安 dependsOnLevelValue所述的属性 元件和 所述的属性 元素。<Property>uniqueKeyLevelName<Hierarchy>

dependsOnLevelValue一个的属性 成员属性用于指示该成员属性的值在功能上依赖于值该成员属性被定义英寸 换句话说,对于一个给定的价值水平,财产的价值是不变的。<Level>

a的uniqueKeyLevelName属性 用来表示给定的级别(如果有的话)和层级中的所有更高级别一起作为唯一的备用关键字,确保对于这些关卡值的任何独特组合,确切地说有一个值的组合对于它下面的所有级别。 <Hierarchy>

为了说明,请考虑在美国建立和许可的汽车的层次结构模型:

<Dimension name="Automotive" foreignKey="auto_dim_id">

<Hierarchy hasAll="true" primaryKey="auto_dim_id" uniqueKeyLevelName="Vehicle Identification Number">

<Table name="automotive_dim"/>

<Level name="Make" column="make_id" type="Numeric"/>

<Level name="Model" column="model_id" type="Numeric"/>

<Level name="ManufacturingPlant" column="plant_id" type="Numeric"/>

<Property name="State" column="plant_state_id" type="Numeric" dependsOnLevelValue="true"/>

<Property name="City" column="plant_city_id" type="Numeric" dependsOnLevelValue="true"/>

<Level name="Vehicle Identification Number" column="vehicle_id" type="Numeric"/>

<Property name="Color" column="color_id" type="Numeric" dependsOnLevelValue="true"/>

<Property name="Trim" column="trim_id" type="Numeric" dependsOnLevelValue="true"/>

<Level name="LicensePlateNum" column="license_id" type="String"/>

<Property name="State" column="license_state_id" type="Numeric" dependsOnLevelValue="true"/>

</Hierarchy>

</Dimension>

在上面的例子中,我们知道一个给定的制造工厂只存在于一个城市和一个州,一辆给定的汽车只有一个配色方案和一个配平等级,并且许可号码与一个单一的状态相关联。因此,我们可以说所有这些成员属性在功能上都依赖于关联的级别值。

另外,我们知道车辆识别号码唯一标识每辆车,并且每辆车只有一个许可证。因此,我们知道,品牌,型号,制造工厂和车辆识别号码的组合唯一地识别每个车辆; 许可证号码是多余的。

这些属性可以在Mondrian生成的SQL语句中优化GROUP BY子句。在缺少任何功能依赖信息的情况下,Automotive维度上的典型查询将如下所示:

SELECT

`automotive_dim`.`make_id` AS c0,

`automotive_dim`.`model_id` AS c1,

`automotive_dim`.`plant_id` AS c2,

`automotive_dim`.`plant_state_id` AS c3,

`automotive_dim`.`plant_city_id` AS c4,

`automotive_dim`.`vehicle_id` AS c5,

`automotive_dim`.`color_id` AS c6,

`automotive_dim`.`trim_id` AS c7,

`automotive_dim`.`license_id` AS c8,

`automotive_dim`.`license_state_id` AS c9

FROM

`automotive_dim` AS `automotive_dim`,

GROUP BY

`automotive_dim`.`make_id`,

`automotive_dim`.`model_id`,

`automotive_dim`.`plant_id`,

`automotive_dim`.`plant_state_id`,

`automotive_dim`.`plant_city_id`,

`automotive_dim`.`vehicle_id`,

`automotive_dim`.`color_id`,

`automotive_dim`.`trim_id`,

`automotive_dim`.`license_id`,

`automotive_dim`.`license_state_id`

ORDER BY

`...

但是,假设上面的模式示例中的函数依赖属性,我们知道查询是在包含“唯一键”级别的深度进行选择的,并且查询中的所有属性在功能上也依赖于它们的级别。在这种情况下,GROUP BY子句是多余的,可能会被完全消除,从而显着提高某些数据库的SQL查询性能:

SELECT

`automotive_dim`.`make_id` AS c0,

`automotive_dim`.`model_id` AS c1,

`automotive_dim`.`plant_id` AS c2,

`automotive_dim`.`plant_state_id` AS c3,

`automotive_dim`.`plant_city_id` AS c4,

`automotive_dim`.`vehicle_id` AS c5,

`automotive_dim`.`color_id` AS c6,

`automotive_dim`.`trim_id` AS c7,

`automotive_dim`.`license_id` AS c8,

`automotive_dim`.`license_state_id` AS c9

FROM

`automotive_dim` AS `automotive_dim`,

ORDER BY

`...

如果查询的深度不足以包含“唯一键”级别,或者没有任何成员属性在功能上依赖于其级别,则无法进行优化。

在某些情况下,可以在没有“唯一键”级别的情况下进行不同的优化,但是某些或全部成员属性在功能上依赖于它们的级别。一些数据库(特别是MySQL)允许在SELECT子句中列出不列在GROUP BY子句中的列。在这样的数据库上,Mondrian可以简单地将功能上依赖的成员属性留在GROUP BY之外,这可以大大减少SQL查询处理时间:

SELECT

`automotive_dim`.`make_id` AS c0,

`automotive_dim`.`model_id` AS c1,

`automotive_dim`.`plant_id` AS c2,

`automotive_dim`.`plant_state_id` AS c3,

`automotive_dim`.`plant_city_id` AS c4,

`automotive_dim`.`vehicle_id` AS c5,

`automotive_dim`.`color_id` AS c6,

`automotive_dim`.`trim_id` AS c7,

`automotive_dim`.`license_id` AS c8,

`automotive_dim`.`license_state_id` AS c9

FROM

`automotive_dim` AS `automotive_dim`,

GROUP BY

`automotive_dim`.`make_id`,

`automotive_dim`.`model_id`,

`automotive_dim`.`plant_id`,

`automotive_dim`.`vehicle_id`,

`automotive_dim`.`license_id`,

ORDER BY

`...

请注意,Mondrian 4.0中的模式语法预计将发生重大变化,包括声明函数依赖性的新方法。虽然预期4.0架构处理器将保持与为Mondrian 3.1开发的架构的向后兼容性,但是这些是为了在此期间提供支持而引入的过渡属性,并且4.0将不会与它们向后兼容。因此,使用这些属性的任何模式都需要迁移到新的语法,作为升级到Mondrian 4.0的一部分。

4.物理模式 

物理模式定义了一组表格,列和它们之间的关系。关系被称为链接。

物理模式的目标是将逻辑模式(立方体,维度,度量等)与实际数据库分离。例如,度量可以基于列,并且不需要知道它是使用SQL表达式计算的实际列还是列。

由于存在隔离层,因此可以更改实现。例如,您可以将计算列更改为实际列,也可以将查询或内联表更改为实际表,而不更改逻辑模式。

4.1表 

表是数据库表的命名用途。

表使用元素声明。如果事实表不在默认模式中,则可以使用“模式”属性提供显式模式; 例如:<Table>

<Table schema="Foodmart" name="sales_fact_1997"/>

4.2列和计算列 

在表格中,您可以选择定义 元素。例如:<Column>

<Table name='customer'>

<ColumnDefs>

<ColumnDef name='customer_id' type='Integer' internalType='int'/>

<ColumnDef name='fname'/>

<ColumnDef name='lname'/>

<CalculatedColumnDef name='full_name' type='String'>

<ExpressionView>

<SQL dialect='mysql'>

CONCAT(<Column name='fname'/>, ' ', <Column name='lname'/>)

</SQL>

<SQL dialect='generic'>

<Column name='fullname'/>

</SQL>

</ExpressionView>

</CalculatedColumnDef>

</ColumnDefs>

</Table>

如果您不包含章节,Mondrian将从JDBC读取列定义。这种单行表定义对于大多数目的来说是足够的。<ColumnDefs>

该 声明的表有一个列,我们要使用它作为一个整数。这将影响列的排序方式,并影响从此列构建的MDX类型的表达式。底层列可能具有不同的类型,如 或。<ColumnDef name='customer_id' type='Integer' internalType='int'>customercustomer_idFLOATVARCHAR(20)

该属性internalType='int'告诉蒙德里安如何在内部表示值。值int意味着Mondrian将使用getIntJDBC调用来访问这些值,并使用Java int值存储它们。

接下来的两个要素只是说明了我们期望和 列存在的事实。<ColumnDef>fnamelname

该定义使用SQL表达式的列。请注意,您可以包含多个方言的SQL。Mondrian将为当前的后端数据库选择正确的SQL表达式。(这在定义一个需要在多个后端数据库上保持不变的打包应用程序时非常有用。)<CalculatedColumnDef>

SQL可以包含嵌入的元素。Mondrian将这些引用替换为列的引用,使用表别名进行限定,并为当前SQL方言适当地引用标识符。(这可能会在MySQL或Oracle上。<Column>`customer`.`customer_id`"customer"."customer_id"

您也可以使用该 构造来构建更复杂的SQL语句。<Query>

4.3内联表 

该构造允许您在模式文件中定义数据集。您必须声明列的名称,列类型(“字符串”或“数字”)和一组行。至于 和 ,你必须提供一个唯一的别名来引用数据集。<InlineTable><Table><View>

这里是一个例子:

<Dimension name="Severity">

<Hierarchy hasAll="true" primaryKey="severity_id">

<InlineTable alias="severity">

<ColumnDefs>

<ColumnDef name="id" type="Numeric"/>

<ColumnDef name="desc" type="String"/>

</ColumnDefs>

<Rows>

<Row>

<Value column="id">1</Value>

<Value column="desc">High</Value>

</Row>

<Row>

<Value column="id">2</Value>

<Value column="desc">Medium</Value>

</Row>

<Row>

<Value column="id">3</Value>

<Value column="desc">Low</Value>

</Row>

</Rows>

</InlineTable>

<Level name="Severity" column="id" nameColumn="desc" uniqueMembers="true"/>

</Hierarchy>

</Dimension>

这与在数据库中有一个名为“严重性”的表具有相同的效果:

ID降序
1
2
3

和声明

<Dimension name="Severity">

<Hierarchy hasAll="true" primaryKey="severity_id">

<Table name="severity"/>

<Level name="Severity" column="id" nameColumn="desc" uniqueMembers="true"/>

</Hierarchy>

</Dimension>

要为列指定NULL值,请省略该<Value> 列,并且列的值将默认为NULL。

4.4查询 

一个元素通过执行一个SQL语句来定义一个表。它与RDBMS中的视图定义类似。至于 ,你可以为不同的SQL方言提供不同的SQL表达式。<Query><CalculatedColumnDef>

例:

<Query name='american_customers'>

<ExpressionView>

<SQL dialect='generic'>

SELECT * FROM customer WHERE country = 'USA'

</SQL>

</ExpressionView>

</Query>

4.5链接 

A 定义了两个表之间的关系。这种关系是指向的,就像外键一样。当在雪花维度中使用表格时,它将用作自动连接路径。<Link>

这里是如何定义臭名昭着的emp 和dept表之间的联系。

<Table name='emp'>

<Key>

<Column name='empno'/>

</Key>

</Table>

<Table name='dept'>

<Key>

<Column name='deptno'/>

</Key>

</Table>

<Link target='emp' source='dept'>

<ForeignKey>

<Column name='deptno'/>

</ForeignKey>

</Link>

这里是一个更短的,相当于使用keyColumnforeignKeyColumn 属性的版本。这些捷径是可能的,因为这些键不是合成的。

<Table name='emp' keyColumn='empno'/>

<Table name='dept' keyColumn='deptno'/>

<Link target='emp' source='dept' foreignKeyColumn='deptno'/>

Mondrian模式中的其他一些table-to-table连接不会自动使用链接。例如,您需要提供一个显式 连接 事实表到维度表。<ForeignKeyLink>sales_fact_1997customer

(Rationale as follows. If we used links for purposes such as connecting fact to dimension tables, many schemas would become cyclic or ambiguous (there might be multiple paths between various tables). But we still think it is useful to be able define links in a diagram, as a guideline for what join paths make sense. After all, doesn't everyone wish there were a schema diagram for every schema? A schema modeler would, we hope, include a diagramming tool, and if links are present, it would suggest that they are used when defining connections in the schema.)

4.6 Table Hints 

Mondrian supports a limited set of database-specific hints for the <Table> element, which will then be passed on to SQL queries involving the table. These hints are as follows:

DatabaseHint TypePermitted ValuesDescription
MySQLforce_indexThe name of an index on this tableForces the named index to be used when selecting level values from this table.

For example:

<Table name="automotive_dim">

<Hint type="force_index">my_index</Hint>

</Table>

As with the functional dependency optimizations, support for table hints is in a transitional stage, and are likely to change in Mondrian 4.0. Any schema using them may need to be migrated to the new schema syntax as part of upgrading to Mondrian 4.0.

4. Star and snowflake schemas 

We saw earlier how to build a cube based upon a fact table, and dimensions in the fact table ("Payment method") and in a table joined to the fact table ("Gender"). This is the most common kind of mapping, and is known as a star schema.

但是一个维度可以基于多个表格,只要有一个明确定义的路径将这些表格连接到事实表格。这种维度被称为雪花,并使用操作符来定义。例如:<Join>

<Cube name="Sales">

...

<Dimension name="Product" foreignKey="product_id">

<Hierarchy hasAll="true" primaryKey="product_id" primaryKeyTable="product">

<Join leftKey="product_class_key" rightAlias="product_class" rightKey="product_class_id">

<Table name="product"/>

<Join leftKey="product_type_id" rightKey="product_type_id">

<Table name="product_class"/>

<Table name="product_type"/>

</Join>

</Join>

<!-- Level declarations ... -->

</Hierarchy>

</Dimension>

</Cube>

这定义了"Product"由三个表组成的维度。事实表加入"product"(通过外键 "product_id"),加入"product_class"(通过外键"product_class_id"),加入" product_type"(通过外键"product_type_id")。我们需要<Join>嵌套在<Join> 元素中的元素,因为<Join>需要两个操作数; 操作数可以是表,连接,甚至查询。

桌子的安排似乎很复杂,简单的经验法则是按照它们包含的行数排序表。该"product"表有行数最多,因此将其加入到事实表和第一次出现; "product_class" 行数较少"product_type",在雪花的尖端,至少有一部分。

请注意,外部<Join>元素有一个 rightAlias属性。这是必要的,因为连接的正确组件(内部 <Join>元素)由多个表组成。leftAlias在这种情况下不需要 属性,因为leftKey列明确地来自"product"表格。

4.1共享维度 

在为连接生成SQL时,Mondrian需要知道要加入的列。如果你正在加入一个连接,那么你需要告诉它该连接属于哪个表(通常它是连接中的第一个表)。

由于共享维度不属于多维数据集,因此必须为其提供一个明确的表格(或其他数据源)。在特定的多维数据集中使用它们时,可以指定外键。此示例显示使用外键Store TypeSales多维数据集连接到 多维数据集sales_fact_1997.store_id ,以及Warehouse使用warehouse.warehouse_store_id外键连接多维数据集 :

<Dimension name="Store Type">

<Hierarchy hasAll="true" primaryKey="store_id">

<Table name="store"/>

<Level name="Store Type" column="store_type" uniqueMembers="true"/>

</Hierarchy>

</Dimension>

 

<Cube name="Sales">

<Table name="sales_fact_1997"/>

...

<DimensionUsage name="Store Type" source="Store Type" foreignKey="store_id"/>

</Cube>

 

<Cube name="Warehouse">

<Table name="warehouse"/>

...

<DimensionUsage name="Store Type" source="Store Type" foreignKey="warehouse_store_id"/>

</Cube>

5.高级逻辑构造 

5.1虚拟立方体 

虚拟立方体组合了两个或更多常规立方体。它由<VirtualCube> 元素定义:

<VirtualCube name="Warehouse and Sales">

<CubeUsages>

<CubeUsage cubeName="Sales" ignoreUnrelatedDimensions="true"/>

<CubeUsage cubeName="Warehouse"/>

</CubeUsages>

<VirtualCubeDimension cubeName="Sales" name="Customers"/>

<VirtualCubeDimension cubeName="Sales" name="Education Level"/>

<VirtualCubeDimension cubeName="Sales" name="Gender"/>

<VirtualCubeDimension cubeName="Sales" name="Marital Status"/>

<VirtualCubeDimension name="Product"/>

<VirtualCubeDimension cubeName="Sales" name="Promotion Media"/>

<VirtualCubeDimension cubeName="Sales" name="Promotions"/>

<VirtualCubeDimension name="Store"/>

<VirtualCubeDimension name="Time"/>

<VirtualCubeDimension cubeName="Sales" name="Yearly Income"/>

<VirtualCubeDimension cubeName="Warehouse" name="Warehouse"/>

<VirtualCubeMeasure cubeName="Sales" name="[Measures].[Sales Count]"/>

<VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Cost]"/>

<VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Sales]"/>

<VirtualCubeMeasure cubeName="Sales" name="[Measures].[Unit Sales]"/>

<VirtualCubeMeasure cubeName="Sales" name="[Measures].[Profit Growth]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Store Invoice]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Supply Time]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Units Ordered]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Units Shipped]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Warehouse Cost]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Warehouse Profit]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Warehouse Sales]"/>

<VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Average Warehouse Sale]"/>

<CalculatedMember name="Profit Per Unit Shipped" dimension="Measures">

<Formula>[Measures].[Profit] / [Measures].[Units Shipped]</Formula>

</CalculatedMember>

</VirtualCube>

该 元素是可选的。它指定了导入虚拟多维数据集的多维数据集。拥有Cube使用元素。<CubeUsages>

该 元素是可选的。它指定导入到虚拟多维数据集中的基本多维数据集。目前,可以定义VirtualCubeMeasure和从基础多维数据集进行的类似导入,而无需为多维数据集定义CubeUsage。该属性指定正在导入的基础多维数据集。该属性指定来自此基本多维数据集的度量将具有未加入维度的成员推送到顶级成员。此行为目前支持聚合。该属性默认为false。 类似于2005年SSAS在类似命名的功能的实验性功能 MSDN文档<CubeUsage>cubeNameignoreUnrelatedDimensionsignoreUnrelatedDimensions 提及“当IgnoreUnrelatedDimensions为true时,不相关的维度被强制到最高级别;当值为false时,维度不会被强制到最高级别,这个属性类似于多维表达式(MDX)ValidMeasure函数”。目前蒙德里安的实施 ignoreUnrelatedDimensions取决于使用ValidMeasure。例如,如果我们想要将此行为应用到“仓库和销售”虚拟立方体中的“单位销售”度量,那么我们需要为上述示例中所示的“销售”立方体定义一个CubeUsage条目,并用ValidMeasure包装此度量。

该 元件从构成立方体的一个出口的尺寸。如果您不指定该属性,则表示您正在导入共享维度。(如果多维数据集中多次使用共享维度,则目前无法消除您打算导入的共享维度的使用情况。)<VirtualCubeDimension>cubeName

该 元件进口的量度从构成立方体中的一个。它是用相同的名称导入的。如果您想创建一个公式,或者只是在导入时重命名一个度量,请使用该 元素。<VirtualCubeMeasure><CalculatedMember>

在现实世界的应用程序中,虚拟立方体出奇地频繁出现。当你有不同粒度的事实表(比如一个在日期水平,另一个在月份水平)或者不同维度的事实表(比如产品,时间和客户,产品,时间和仓库的另一个)并希望将结果呈现给不了解或关心数据结构的最终用户。

任何常见维度 - 两个组成立方体使用的共享维度 - 会自动同步。在这个例子中,[Time] 并且[Product]是常见的维度。因此,如果上下文是([Time].[1997].[Q2], [Product].[Beer].[Miller Lite]),则来自任一个多维数据集的度量都将与此上下文相关。

仅属于一个立方体的尺寸称为不合格尺寸。该[Gender]尺寸是这样一个例子:它存在于Sales立方体,但没有Warehouse。如果上下文是([Gender].[F][Time].[1997].[Q1]),那么询问[Unit Sales]度量值(来自 [Sales]多维数据集)而不是[Units Ordered]度量值(来自[Warehouse])是有意义的。在上下文中[Gender].[F][Units Ordered]有值NULL。

5.2父子层次结构 

传统的层次结构有一组严格的层次,而成员则坚持这些层次。例如,在Product层次结构中,Product Name 关卡中的任何成员都具有关卡中的父项,并且在Brand Name关卡中具有父项, Product Subcategory等等。这种结构有时过于僵化,无法模拟真实世界的数据。

一个父子层次结构只有一个级别(不包括特殊的“全部”级别),但任何成员都可以在同一个水平有父母。一个典型的例子是Employees层次结构中的报告结构:

<Dimension name="Employees" foreignKey="employee_id">

<Hierarchy hasAll="true" allMemberName="All Employees" primaryKey="employee_id">

<Table name="employee"/>

<Level name="Employee Id" uniqueMembers="true" type="Numeric" column="employee_id" nameColumn="full_name" parentColumn="supervisor_id" nullParentValue="0">

<Property name="Marital Status" column="marital_status"/>

<Property name="Position Title" column="position_title"/>

<Property name="Gender" column="gender"/>

<Property name="Salary" column="salary"/>

<Property name="Education Level" column="education_level"/>

<Property name="Management Role" column="management_role"/>

</Level>

</Hierarchy>

</Dimension>

最重要的属性,这里有parentColumnnullParentValue

  • parentColumn属性是将成员链接到其父成员的列的名称; 在这种情况下,就是指向一名员工的主管的外键列。该<ParentExpression> 子元素<Level>相当于parentColumn 属性,但允许你定义一个SQL表达式,就像<Expression>元素。的parentColumn 属性(或<ParentExpression>元件)是蒙德里安一个层级具有母子结构的唯一指示。
  • nullParentValue属性是指示成员没有父项的值。缺省值是nullParentValue="null",但是由于许多数据库不会索引空值,因此模式设计人员有时会将值用作空字符串0和-1。

5.2.1调整父子层次结构 

上面定义的父子层次结构存在一个严重的问题,那就是Mondrian为了计算单元总数所做的工作量。假设雇员表包含以下数据:

雇员
supervisor_id员工ID全名
空值1坦率
12法案
23埃里克
14
3标记
26卡拉

If we want to compute the total salary budget for Bill, we need to add in the salaries of Eric and Carla (who report to Bill) and Mark (who reports to Eric). Usually Mondrian generates a SQL GROUP BY statement to compute these totals, but there is no (generally available) SQL construct which can traverse hierarchies. So by default, Mondrian generates one SQL statement per supervisor, to retrieve and total all of that supervisor's direct reports.

This approach has a couple of drawbacks. First, the performance is not very good if a hierarchy contains more than a hundred members. Second, because Mondrian implements the distinct-count aggregator by generating SQL, you cannot define a distinct-count measure in any cube which contains a parent-child hierarchy.

我们如何解决这些问题?答案是增强数据,以便Mondrian能够使用标准SQL检索所需的信息。Mondrian支持一种称为封闭表的机制。

5.2.2关闭表 

闭包表是一个SQL表,其中包含每个员工/主管关系的记录,无论深度如何。(用数学术语来说,这被称为员工/主管关系的“自反传递闭包”,这个distance 列并不是严格要求的,但是它使填充表更容易)。

employee_closure
supervisor_id员工ID距离
110
121
132
141
13
162
220
231
22
261
330
31
440
0
660

在目录XML中,元素将该级别映射到:<Closure><Table>

<Dimension name="Employees" foreignKey="employee_id">

<Hierarchy hasAll="true" allMemberName="All Employees" primaryKey="employee_id">

<Table name="employee"/>

<Level name="Employee Id" uniqueMembers="true" type="Numeric"
column="employee_id" nameColumn="full_name" parentColumn="supervisor_id" nullParentValue="0">

<Closure parentColumn="supervisor_id" childColumn="employee_id">

<Table name="employee_closure"/>

</Closure>

<Property name="Marital Status" column="marital_status"/>

<Property name="Position Title" column="position_title"/>

<Property name="Gender" column="gender"/>

<Property name="Salary" column="salary"/>

<Property name="Education Level" column="education_level"/>

<Property name="Management Role" column="management_role"/>

</Level>

</Hierarchy>

</Dimension>

此表允许在纯SQL中评估总计。尽管这会在查询中引入额外的表,但是数据库优化器在处理连接方面非常出色。我建议你声明两个supervisor_idemployee_idNOT NULL,并索引他们如下:

CREATE UNIQUE INDEX employee_closure_pk ON employee_closure (
   supervisor_id,
   employee_id);
CREATE INDEX employee_closure_emp ON employee_closure (
   employee_id);

5.2.3填充闭包表 

每当层次更改时,表格都需要重新填充,这是应用程序的责任 - Mondrian不这样做!

如果您正在使用Pentaho数据集成(Kettle),则有一个特殊的步骤来将封闭表填充为ETL过程的一部分。Pentaho数据集成wiki中的更多细节 。

Pentaho数据集成中的闭包生成器步骤

Pentaho数据集成中的闭包生成器步骤

如果您不使用Pentaho数据集成,则可以使用SQL自行填充表。这里是一个填充闭包表的MySQL存储过程的例子。

DELIMITER //

CREATE PROCEDURE populate_employee_closure()
BEGIN
  DECLARE distance int;
  TRUNCATE TABLE employee_closure;
  SET distance = 0;
  -- seed closure with self-pairs (distance 0)
  INSERT INTO employee_closure (supervisor_id, employee_id, distance)
    SELECT employee_id, employee_id, distance
      FROM employee;

  -- for each pair (root, leaf) in the closure,
  -- add (root, leaf->child) from the base table

  REPEAT
    SET distance = distance + 1;
    INSERT INTO employee_closure (supervisor_id, employee_id, distance)
      SELECT employee_closure.supervisor_id, employee.employee_id, distance
        FROM employee_closure, employee
          WHERE employee_closure.employee_id = employee.supervisor_id
          AND employee_closure.distance = distance - 1;
  UNTIL (ROW_COUNT() == 0))
  END REPEAT;
END //

DELIMITER ;

5.4计算的成员 

假设您要创建一个度量值,其值不是来自事实表的列,而是来自MDX公式。一种方法是使用一个WITH MEMBER子句,如下所示:

WITH MEMBER [Measures].[Profit] AS '[Measures].[Store Sales]-[Measures].[Store Cost]',
   FORMAT_STRING = '$#,###'
SELECT {[Measures].[Store Sales], [Measures].[Profit]} ON COLUMNS,
  {[Product].Children} ON ROWS
FROM [Sales]
WHERE [Time].[1997]

但是,不要将此子句包含在应用程序的每个MDX查询中,都可以将模式中的成员定义为多维数据集定义的一部分:

<CalculatedMember name="Profit" dimension="Measures">

<Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>

<CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>

</CalculatedMember>

如果您愿意,也可以将公式声明为XML属性。效果是一样的。

<CalculatedMember name="Profit" dimension="Measures" formula="[Measures].[Store Sales]-[Measures].[Store Cost]">

<CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>

</CalculatedMember>

请注意, (not )元素对应于MDX语句的片段。您也可以在这里定义其他属性,但在实践中是最有用的。<CalculatedMemberProperty><Property>FORMAT_STRING = '$#,###'FORMAT_STRING

The FORMAT_STRING property value can also be evaluated using an expression. When formatting a particular cell, first the expression is evaluated to yield a format string, then the format string is applied to the cell value. Here is the same property with a conditional format string:

<CalculatedMemberProperty name="FORMAT_STRING" expression="Iif(Value < 0, '|($#,##0.00)|style=red', '|$#,##0.00|style=green')"/>

For more details about format strings, see the MDX specification.

One additional calculated member property that is worth mentioning is DATATYPE. As with measures, setting datatype specifies how the calculated member is returned via XML for Analysis. The DATATYPE property of a calculated member can have values "String", "Integer", or "Numeric":

<CalculatedMemberProperty name="DATATYPE" value="Numeric"/>

您可以为计算的成员属性指定SOLVE_ORDER。解决顺序决定了竞争表达式中计算的优先级

<CalculatedMemberProperty name="SOLVE_ORDER" value="2000"/>

您可以使计算的成员或措施不可见。如果visible="false" 在or 元素中指定(默认值为“true”),JPivot等用户界面会注意到该属性并隐藏该成员。如果您想在多个步骤中执行计算,并从最终用户隐藏中间步骤,这非常有用。例如,这里只有“每平方尺的保证金”是可见的,它的因子“店面成本”,“保证金”和“店铺平面”是隐藏的:<Measure><CalculatedMember>

<Measure name="Store Cost" column="store_cost" aggregator="sum" formatString="#,###.00" visible="false"/>

<CalculatedMember name="Margin" dimension="Measures" visible="false">

<Formula>([Measures].[Store Sales] - [Measures].[Store Cost]) / [Measures].[Store Cost]</Formula>

</CalculatedMember>

<CalculatedMember name="Store Sqft" dimension="Measures" visible="false">

<Formula>[Store].Properties("Sqft")</Formula>

</CalculatedMember>

<CalculatedMember name="Margin per Sqft" dimension="Measures" visible="true">

<Formula>[Measures].[Margin] / [Measures].[Store Cost]</Formula>

<CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/>

</CalculatedMember>

5.5命名集 

WITH SETMDX语句的子句允许您声明可在整个查询中使用的集合表达式。例如,

WITH SET [Top Sellers] AS 
    'TopCount([Warehouse].[Warehouse Name].MEMBERS, 5, [Measures].[Warehouse Sales])'
SELECT 
    {[Measures].[Warehouse Sales]} ON COLUMNS,
    {[Top Sellers]} ON ROWS
FROM [Warehouse]
WHERE [Time].[Year].[1997]

这个WITH SET子句WITH MEMBER和这个子句非常相似,正如你所期望的那样,它在模式上有一个类似的构造 。该 元素允许您在模式中定义一个命名集作为多维数据集定义的一部分。对于针对该多维数据集的任何查询,它都是隐式可用的:<CalculatedMember><NamedSet>

<Cube name="Warehouse">

...

<NamedSet name="Top Sellers">

<Formula>TopCount([Warehouse].[Warehouse Name].MEMBERS, 5, [Measures].[Warehouse Sales])</Formula>

</NamedSet>

</Cube>


SELECT 
    {[Measures].[Warehouse Sales]} ON COLUMNS,
    {[Top Sellers]} ON ROWS
FROM [Warehouse]
WHERE [Time].[Year].[1997]
仓库仓库销售
树屋分布31,116.37
豪尔赫·加西亚公司30,743.77
阿蒂西亚仓储公司29,207.96
约根森服务存储22,869.79
Destination,Inc.22,187.42

针对多维数据集定义的命名集不会被针对该多维数据集定义的虚拟多维数据集继承。(但是你可以根据虚拟立方体来定义一个命名集合。)

您还可以将一个命名集定义为模式的全局:

<Schema>

<Cube name="Sales" ... />

<Cube name="Warehouse" ... />

<VirtualCube name="Warehouse and Sales" .../>

<NamedSet name="CA Cities" formula="{[Store].[USA].[CA].Children}"/>

<NamedSet name="Top CA Cities">

<Formula>TopCount([CA Cities], 2, [Measures].[Unit Sales])</Formula>

</NamedSet>

</Schema>

在架构中定义的命名集可用于该架构中的所有多维数据集和虚拟多维数据集。但是,只有在多维数据集包含使公式有效的名称所需的维度时才有效。例如,[CA Cities]在查询[Sales][Warehouse and Sales]多维[Warehouse]数据集中使用它是有效的,但是如果您在针对多维数据集的查询中使用它,则会出现错误,因为[Warehouse]它没有[Store]维度。

6.插件 

有时Mondrian的模式语言不够灵活,或者MDX语言不够强大,无法解决手头的问题。你想要做的是在Mondrian应用程序中添加一些你自己的Java代码,而插件就是这样做的一种方法。

每个Mondrian的扩展在技术上都是一个服务提供者接口(SPI); 简而言之,就是编写代码来实现的Java接口,以及Mondrian将在运行时调用的接口。您还需要注册一个扩展(通常在schema.xml文件中的某处),并确保它出现在类路径中。

插件包括 用户定义的功能 ; 单元格成员和 属性格式化程序 ; 动态模式处理器和 数据源更改监听器成员阅读器单元阅读器的支持不完善,将来我们可能会支持可插拔的 SQL方言

一些插件(用户定义函数,成员格式化程序,属性格式化程序,单元格式化程序)可以用JavaScript等脚本语言实现。在这种情况下,你不需要编写一个Java类; 您只需将脚本代码放入mondrian模式文件的Script元素中。在脚本语言中实现的扩展通常不像在Java中实现的扩展那么好,但是它们更方便,因为你不需要编译任何代码。只需修改mondrian模式文件中的脚本代码并重新加载模式即可。较短的代码 - 调试 - 修复周期使您能够更快地开发应用程序。一旦在脚本中实现了插件,如果性能仍然是一个问题,则可以将插件转换为Java。

其他扩展包括动态数据源XMLA servlet

6.1用户定义的功能 

用户定义的函数必须具有公共构造函数并实现 mondrian.spi.UserDefinedFunction接口。例如,

package com.example;

import mondrian.olap.*;
import mondrian.olap.type.*;
import mondrian.spi.UserDefinedFunction;

/**
 * A simple user-defined function which adds one to its argument.
 */
public class PlusOneUdf implements UserDefinedFunction {
    // public constructor
    public PlusOneUdf() {
    }

    public String getName() {
        return "PlusOne";
    }

    public String getDescription() {
        return "Returns its argument plus one";
    }

    public Syntax getSyntax() {
        return Syntax.Function;
    }

    public Type getReturnType(Type[] parameterTypes) {
        return new NumericType();
    }

    public Type[] getParameterTypes() {
        return new Type[] {new NumericType()};
    }

    public Object execute(Evaluator evaluator, Exp[] arguments) {
        final Object argValue = arguments[0].evaluateScalar(evaluator);
        if (argValue instanceof Number) {
            return new Double(((Number) argValue).doubleValue() + 1);
        } else {
            // Argument might be a RuntimeException indicating that
            // the cache does not yet have the required cell value. The
            // function will be called again when the cache is loaded.
            return null;
        }
    }

    public String[] getReservedWords() {
        return null;
    }
}

在你的模式中声明它:

<Schema ...>

...

<UserDefinedFunction name="PlusOne" className="com.example.PlusOneUdf"/>

</Schema>

并在任何MDX语句中使用它:

WITH MEMBER [Measures].[Unit Sales Plus One] 
    AS 'PlusOne([Measures].[Unit Sales])'
SELECT
    {[Measures].[Unit Sales]} ON COLUMNS,
    {[Gender].MEMBERS} ON ROWS
FROM [Sales]

如果用户定义的函数具有一个带有一个字符串参数的公共构造函数,则Mondrian将传入该函数的名称。为什么?这使您可以使用相同的类来定义两个或更多用户定义的函数:

package com.example;

import mondrian.olap.*;
import mondrian.olap.type.*;
import mondrian.spi.UserDefinedFunction;

/**
 * A user-defined function which either adds one to or 
 * subtracts one from its argument.
 */
public class PlusOrMinusOneUdf implements UserDefinedFunction {
    private final name;
    private final isPlus;

    // public constructor with one argument
    public PlusOneUdf(String name) {
        this.name = name;
        if (name.equals("PlusOne")) {
            isPlus = true;
        } else if (name.equals("MinusOne")) {
            isPlus = false;
        } else {
            throw new IllegalArgumentException("Unexpected name " + name);
        }
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return "Returns its argument plus or minus one";
    }

    public Syntax getSyntax() {
        return Syntax.Function;
    }

    public Type getReturnType(Type[] parameterTypes) {
        return new NumericType();
    }

    public Type[] getParameterTypes() {
        return new Type[] {new NumericType()};
    }

    public Object execute(Evaluator evaluator, Exp[] arguments) {
        final Object argValue = arguments[0].evaluateScalar(evaluator);
        if (argValue instanceof Number) {
            if (isPlus) {
                return new Double(((Number) argValue).doubleValue() + 1);
            } else {
                return new Double(((Number) argValue).doubleValue() - 1);
            }
        } else {
            // Argument might be a RuntimeException indicating that
            // the cache does not yet have the required cell value. The
            // function will be called again when the cache is loaded.
            return null;
        }
    }

    public String[] getReservedWords() {
        return null;
    }
}

并在您的模式中注册两个函数:

<Schema ...>

...

<UserDefinedFunction name="PlusOne" className="com.example.PlusOrMinusOneUdf">

<UserDefinedFunction name="MinusOne" className="com.example.PlusOrMinusOneUdf">

</Schema>

如果您厌倦了在模式文件中编写重复的用户定义的函数声明,则可以将用户定义的函数实现类打包为具有嵌入式META-INF/services/mondrian.spi.UserDefinedFunction 资源文件的jar 文件。此资源文件包含接口mondrian.spi.UserDefinedFunction的实现的类名,每行一个名称。有关更多详细信息,可以查看 JAR文件规范src/main/META-INF/services/mondrian.spi.UserDefinedFunction 的源代码分发和 服务提供者部分。用这种方法声明的用户定义的函数可用于JVM中的所有mondrian模式。

警告:当以这种方式声明用户定义函数时,不能在一个类中定义多个用户定义的函数实现。为每个类加载一个函数,并给出该getName()方法返回的名称。

用户定义的函数也可以用脚本语言实现,比如JavaScript。这些函数可能表现不如Java UDF或内置函数,但它们实现起来要方便得多。

要在脚本中定义UDF,请使用Script元素并在其中包含followimg函数:

  • getName()
  • getDescription()
  • getSyntax()
  • getParameterTypes()
  • getReturnType(parameterTypes)
  • execute(evaluator, arguments)

getName(),getDescription(),getReservedWords()和getSyntax()方法是可选的; 的getName()默认为UserDefinedFunction元素的name属性,getDescription()默认为名称,getReservedWords()返回空列表,并getSyntax()默认为mondrian.olap.Syntax.Function。其他方法与UserDefinedFunction SPI中的方法有相似的含义。

下面是JavaScript UDF的阶乘函数的一个例子:

<UserDefinedFunction name="Factorial">

<Script language="JavaScript">

function getParameterTypes() {

return new Array(new mondrian.olap.type.NumericType());

}

function getReturnType(parameterTypes) {

return new mondrian.olap.type.NumericType();

}

function execute(evaluator, arguments) {

var n = arguments[0].evaluateScalar(evaluator);

return factorial(n);

}

function factorial(n) {

return n <= 1 ? 1 : n * factorial(n - 1);

}

</Script>

</UserDefinedFunction>

6.4单元格式化器 

单元格格式化程序修改的行为。类必须实现这个接口,并且是这样指定的: Cell.getFormattedValue() mondrian.spi.CellFormatter

<Measure name="name">

<CellFormatter className="com.example.MyCellFormatter"/>

</Measure>

(之前的语法,使用Measure元素的'formatter'属性,已被弃用,并将在mondrian-4.0中被删除。)

您可以使用脚本元素以JavaScript等脚本语言指定格式化程序:

<Measure name="name">

<CellFormatter>

<Script language="JavaScript">

</Script>

</CellFormatter>

</Measure>

该脚本有一个value变量,对应于 mondrian.spi.CellFormatter.formatCell(Object value) 方法的参数。代码片段可以有多个语句,但必须以return语句结束。

对于属于多维数据集或虚拟多维数据集的计算成员,还可以使用CellFormatter 元素:

<CalculatedMember name="name" dimension="dimension">

<Formula>

[Measures].[Unit Sales] * 2

</Formula>

<CellFormatter>

<Script language="JavaScript">

var s = value.toString();

while (s.length() < 20) {

s = "0" + s;

}

return s;

</Script>

</CellFormatter>

</Measure>

您还可以通过将CELL_FORMATTER成员的属性设置为格式化程序类的名称来定义格式程序。

<CalculatedMember name="name" formatter="com.example.MyCellFormatter">

<CalculatedMemberProperty name="CELL_FORMATTER" value="com.example.MyCellFormatter"/>

</CalculatedMember>

对于在WITH MEMBERMDX查询的子句中定义的计算度量,可以在MDX中设置相同的属性以实现相同的效果:

WITH MEMBER [Measures].[Foo]
  AS '[Measures].[Unit Sales] * 2',
   CELL_FORMATTER='com.example.MyCellFormatter'
SELECT {[Measures].[Unit Sales], [Measures].[Foo]} ON COLUMNS,
    {[Store].Children} ON ROWS
FROM [Sales]

要定义脚本格式程序,请使用CELL_FORMATTER_SCRIPT 和CELL_FORMATTER_SCRIPT_LANGUAGE属性:

WITH MEMBER [Measures].[Foo]
  AS '[Measures].[Unit Sales] * 2',
   CELL_FORMATTER_SCRIPT_LANGUAGE='JavaScript',
   CELL_FORMATTER_SCRIPT='var s = value.toString(); while (s.length() < 20) s = "0" + s; return s;'
SELECT {[Measures].[Unit Sales], [Measures].[Foo]} ON COLUMNS,
    {[Store].Children} ON ROWS
FROM [Sales]

如果成员不属于[Measures]维度,则单元格格式化程序属性将被忽略 。

6.5成员格式化器 

成员格式化程序修改的行为。类必须实现这个接口,并且是这样指定的: Member.getCaption() mondrian.spi.MemberFormatter

<Level name="name" column="column">

<MemberFormatter className="com.example.MyMemberFormatter"/>

</Level>

(使用Level元素的'formatter'属性的以前的语法已被弃用,并将在mondrian-4.0中被删除。)

您可以使用脚本元素以JavaScript等脚本语言指定格式化程序:

<Level name="name" column="column">

<MemberFormatter>

<Script language="JavaScript">

return member.getName().toUpperCase();

</Script>

</MemberFormatter>

</Level>

该脚本有一个member变量,对应于 mondrian.spi.MemberFormatter.formatMember(Member member) 方法的参数。代码片段可以有多个语句,但必须以return语句结束。

6.6属性格式化程序 

属性格式化程序修改的行为。类必须实现这个接口,并且是这样指定的: Property.getPropertyFormattedValue() mondrian.spi.PropertyFormatter

<Attribute name="My Attribute" column="attributeColumn" uniqueMembers="true">

<Property name="My Property" column="propColumn">

<PropertyFormatter className="com.example.MyPropertyFormatter"/>

</Property

<Attribute/>

您可以使用脚本元素以JavaScript等脚本语言指定格式化程序:

<Level name="name" column="column">

<Property name="MyProp" column="PropColumn">

<PropertyFormatter>

<Script language="JavaScript">

return member.getName().toUpperCase();

</Script>

</PropertyFormatter>

</Property>

</Level>

该脚本有可用的memberpropertyName 和propertyValue变量,对应于所述的参数mondrian.spi.PropertyFormatter.formatProperty(会员构件,字符串propertyName的,对象的PropertyValue)方法。代码片段可以有多个语句,但必须以return语句结束。

6.7动态模式处理器 

动态模式处理器实现该接口。它被指定为连接字符串的一部分,如下所示: mondrian.spi.DynamicSchemaProcessor

Jdbc=jdbc:odbc:MondrianFoodMart; JdbcUser=ziggy; JdbcPassword=stardust; DynamicSchemaProcessor=com.example.MySchemaProcessor

其效果是,当从URL中读取模式的内容时,Mondrian转向模式处理器而不是Java的默认URL处理器。这使架构读者有机会通过过滤器运行架构,甚至可以即时生成整个架构。

何时DynamicSchemaProcessor被指定,模式将被处理并在每个ROLAP连接请求上重新加载。属性 UseContentChecksum应与模式处理器一起使用,以启用模式的高速缓存:

DataSource=java:/jdbc/MyWarehouse; DynamicSchemaProcessor=com.example.MySchemaProcessor; UseContentChecksum=true

在这种情况下,一旦加载模式将被缓存,直到它改变。如果模式内容发生更改,则会重新加载。(新加载的模式被认为是一个不同的模式,并将以空的缓存开始。

动态模式是一个非常强大的构造。我们将会看到,它们的重要应用是国际化

6.9动态数据源XMLA servlet 

DynamicDatasourceXmlaServlet延伸DefaultXmlaServlet,加入到动态加载能力datasources.xml的文件。对于它收到的每个客户端请求,它检查内容的更新datasources.xml。它有选择地清除已更改或不再存在的目录的缓存datasources.xml。该servlet认为目录当任其属性(DataSourceInfo,在定义属性的改变作为 DataSourcesConfig.Catalog)是不同的。它通过名称来识别目录。

这个servlet补充了基于UseContentChecksum的动态目录加载功能 。它不检查目录内容的更新。功能没有重叠。两者将共同提供全面的动态数据源和目录配置功能。

要使用DynamicDatasourceXmlaServlet,请在以下位置更改MondrianXmlaServlet servlet的定义web.xml

<servlet>
   <servlet-name>MondrianXmlaServlet</servlet-name>
   <servlet-class>mondrian.xmla.impl.DynamicDatasourceXmlaServlet</servlet-class>
    ...
</servlet>

这个实现有一个限制。它要求目录名称在所有数据源中是唯一的,否则可能无法正常工作。

7.国际化 

一个国际化的蒙德里安应用程序将有一个每种语言的架构,每个对象的标题以本地语言显示。例如,[Product] 维度将会有英文的“产品”和法语的“产品”。

翻译模式对象的实际名称是不明智的,因为MDX语句也需要改变。所有你需要改变的是标题。每个模式对象(模式,多维数据集,虚拟多维数据集,维度,层次结构,级别,度量,命名集)都有一个标题属性,JPivot和Pentaho Analyzer等用户界面显示的是标题而不是真实名称。另外:

  • 每个模式对象都有一个描述属性。
  • 一个层次结构可以有一个allMemberCaption属性作为“全部”成员的显示值。
  • 对于模式,我们可以通过measuresCaption属性设置“度量”维度的显示值。
  • 如果成员是度量值(即Measures维度的成员),则计算的成员具有属性CAPTION和DESCRIPTION,这些属性显示为标题和说明。

创建国际化应用程序的一种方法是为每种语言创建模式文件的副本,但难以维护。更好的方法是使用 LocalizingDynamicSchemaProcessor类在单个模式文件上执行动态替换。

7.1本地化架构处理器

首先,使用变量将您的模式写为,, 和属性的值caption, 如下所示:descriptionallMemberCaptionmeasuresCaption

<Schema ... measuresCaption="%{foodmart.measures.caption}">

<Dimension name="Store" caption="%{foodmart.dimension.store.caption}" description="%{foodmart.dimension.store.description}">

<Hierarchy hasAll="true" allMemberName="All Stores" allMemberCaption="%{foodmart.dimension.store.allmember.caption =All Stores}" primaryKey="store_id"  caption="%{foodmart.hierarchy.store.country.caption}" description="%{foodmart.hierararchy.store.country.description}>

<Table name="store"/>

<Level name="Store Country" column="store_country" uniqueMembers="true" caption="%{foodmart.dimension.store.country.caption}" description="%{foodmart.dimension.store.country.description}"/>

<Level name="Store State" column="store_state" uniqueMembers="true" caption="%{foodmart.dimension.store.state.caption}" description="%{foodmart.dimension.store.state.description}"/>

<Level name="Store City" column="store_city" uniqueMembers="false" caption="%{foodmart.dimension.store.city.caption}" description="%{foodmart.dimension.store.city.description}"/>

<Level name="Store Name" column="store_name" uniqueMembers="true" caption="%{foodmart.dimension.store.name.caption}" description="%{foodmart.dimension.store.name.description}">

<Property name="Store Type" column="store_type" caption="%{foodmart.dimension.store. name.property_type.caption}" description="%{foodmart.dimension.store. name.property_type.description}"/>

<Property name="Store Manager" column="store_manager" caption="%{foodmart.dimension.store. name.property_manager.caption}" description="%{foodmart.dimension.store. name.property_manager.description}"/>

<Property name="Store Sqft" column="store_sqft" type="Numeric" caption="%{foodmart.dimension.store. name.property_storesqft.caption}" description="%{foodmart.dimension.store. name.property_storesqft.description}"/>

<Property name="Grocery Sqft" column="grocery_sqft" type="Numeric"/>

<Property name="Frozen Sqft" column="frozen_sqft" type="Numeric"/>

<Property name="Meat Sqft" column="meat_sqft" type="Numeric"/>

<Property name="Has coffee bar" column="coffee_bar" type="Boolean"/>

<Property name="Street address" column="store_street_address" type="String"/>

</Level>

</Hierarchy>

</Dimension>

 

<Cube name="Sales" caption="%{foodmart.cube.sales.caption}" description="%{foodmart.cube.sales.description}">

...

<DimensionUsage name="Store" source="Store" foreignKey="store_id"  caption="%{foodmart.cube.sales.name.caption}" description="%{foodmart.cube.sales.name.description}"/>

...

<Measure name="Unit Sales" column="unit_sales" caption="%{foodmart.cube.sales.measure.unitsales.caption}" description="%{foodmart.cube.sales.measure.unitsales.description}"/>

</Cube>

</Schema>

像往常一样,没有caption属性的任何多维数据集,度量,维度或级别的默认标题是元素的名称。层次结构的默认标题是其维度的标题; 例如,[Store]层次结构没有caption 定义,所以它继承了caption其父, [Store]维的属性。

接下来,将动态模式处理器和区域设置添加到连接字符串中。例如,

Provider=mondrian; Locale=en_US; DynamicSchemaProcessor=­mondrian.i18n.LocalizingDynamicSchemaProcessor; Jdbc=­jdbc:mysql://localhost/foodmart; JdbcUser=­foodmart; JdbcPassword=­foodmart; Catalog=­/WEB-INF/FoodMart.mondrian.xml

现在,对于您希望支持的每个语言环境,请提供一个名为的资源文件。例如, locale_{locale}.properties

# locale.properties: Default resources
foodmart.measures.caption=Measures
foodmart.dimension.store.country.caption=Store Country
foodmart.dimension.store.name.property_type.column= store_type
foodmart.dimension.store.country.member.caption= store_country
foodmart.dimension.store.name.property_type.caption =Store Type
foodmart.dimension.store.name.caption =Store Name
foodmart.dimension.store.state.caption =Store State
foodmart.dimension.store.name.property_manager.caption =Store Manager
foodmart.dimension.store.name.property_storesqft.caption =Store Sq. Ft.
foodmart.dimension.store.allmember.caption =All Stores
foodmart.dimension.store.caption =Store
foodmart.cube.sales.caption =Sales
foodmart.dimension.store.city.caption =Store City
foodmart.cube.sales.measure.unitsales =Unit Sales

# locale_hu.properties: Resources for the 'hu' locale.
foodmart.measures.caption=Hungarian Measures
foodmart.dimension.store.country.caption=Orsz\u00E1g
foodmart.dimension.store.name.property_manager.caption =\u00C1ruh\u00E1z vezet\u0151
foodmart.dimension.store.country.member.caption =store_country_caption_hu
foodmart.dimension.store.name.property_type.caption =Tipusa
foodmart.dimension.store.name.caption =Megnevez\u00E9s
foodmart.dimension.store.state.caption =\u00C1llam/Megye
foodmart.dimension.store.name.property_type.column =store_type_caption_hu
foodmart.dimension.store.name.property_storesqft.caption =M\u00E9ret n.l\u00E1b
foodmart.dimension.store.allmember.caption =Minden \u00C1ruh\u00E1z
foodmart.dimension.store.caption =\u00C1ruh\u00E1z
foodmart.cube.sales.caption =Forgalom
foodmart.dimension.store.city.caption =V\u00E1ros
foodmart.cube.sales.measure.unitsales =Eladott db

8.聚合表 

当事实表包含大量的行数:百万或更多时,聚集表是提高蒙德里安表现的一种方法。聚合表本质上是事实表中数据的预先计算的摘要。

我们来看一个简单的聚合表。

<Cube name="Sales">

<Table name="sales_fact_1997">

<AggName name="agg_c_special_sales_fact_1997">

<AggFactCount column="FACT_COUNT"/>

<AggMeasure name="[Measures].[Store Cost]" column="STORE_COST_SUM"/>

<AggMeasure name="[Measures].[Store Sales]" column="STORE_SALES_SUM"/>

<AggLevel name="[Product].[Product Family]" column="PRODUCT_FAMILY"/>

<AggLevel name="[Time].[Quarter]" column="TIME_QUARTER"/>

<AggLevel name="[Time].[Year]" column="TIME_YEAR"/>

<AggLevel name="[Time].[Quarter]" column="TIME_QUARTER"/>

<AggLevel name="[Time].[Month]" column="TIME_MONTH"/>

</AggName>

</Table>

 

<!-- Rest of the cube definition -->

</Cube>

这个 元素,这里没有显示,允许你直接引用一个维度表,而不在聚合表中包含它的列。这在聚合表指南中有描述 。<AggForeignKey>

在实践中,基于非常大的事实表的立方体可能有几个聚合表。在模式XML文件中显式声明每个聚合表是不方便的,幸运的是还有更好的办法。在以下示例中,Mondrian通过模式匹配查找聚合表。

<Cube name="Sales">

<Table name="sales_fact_1997">

<AggPattern pattern="agg_.*_sales_fact_1997">

<AggFactCount column="FACT_COUNT"/>

<AggMeasure name="[Measures].[Store Cost]" column="STORE_COST_SUM"/>

<AggMeasure name="[Measures].[Store Sales]" column="STORE_SALES_SUM"/>

<AggLevel name="[Product].[Product Family]" column="PRODUCT_FAMILY"/>

<AggLevel name="[Time].[Quarter]" column="TIME_QUARTER"/>

<AggLevel name="[Time].[Year]" column="TIME_YEAR"/>

<AggLevel name="[Time].[Quarter]" column="TIME_QUARTER"/>

<AggLevel name="[Time].[Month]" column="TIME_MONTH"/>

<AggExclude name="agg_c_14_sales_fact_1997"/>

<AggExclude name="agg_lc_100_sales_fact_1997"/>

</AggPattern>

</Table>

</Cube>

它告诉蒙德里安把所有匹配模式的"agg_.*_sales_fact_1997" 表作为聚合表,除了"agg_c_14_sales_fact_1997" 和 "agg_lc_100_sales_fact_1997"。Mondrian使用规则来推断这些表中的列的角色,所以严格遵守严格的命名约定是很重要的。命名约定在聚合表指南中进行了描述 。

性能指南有关于选择聚合表的建议。

9.访问控制 

好吧,现在你已经掌握了所有这些优秀的数据,但是你并不是每个人都能够阅读所有的数据。为了解决这个问题,你可以定义一个称为角色的访问控制配置文件作为模式的一部分,并在建立连接时设置这个角色。

9.1定义一个角色 

角色是由<Role> 元素定义的,在<Schema> 元素之后,元素是直接的子元素<Cube>。这是一个角色的例子:

<Role name="California manager">

<SchemaGrant access="none">

<CubeGrant cube="Sales" access="all">

<DimensionGrant hierarchy="[Measures]" access="all"/>

<HierarchyGrant hierarchy="[Store]" access="custom" topLevel="[Store].[Store Country]">

<MemberGrant member="[Store].[USA].[CA]" access="all"/>

<MemberGrant member="[Store].[USA].[CA].[Los Angeles]" access="none"/>

</HierarchyGrant>

<HierarchyGrant hierarchy="[Customers]" access="custom" topLevel="[Customers].[State Province]" bottomLevel="[Customers].[City]">

<MemberGrant member="[Customers].[USA].[CA]" access="all"/>

<MemberGrant member="[Customers].[USA].[CA].[Los Angeles]" access="none"/>

</HierarchyGrant>

<HierarchyGrant hierarchy="[Gender]" access="none"/>

</CubeGrant>

</SchemaGrant>

</Role>

<SchemaGrant> 定义模式中对象的默认访问权限。该access属性可以是“全部”或“无”; 这个访问可以被特定的对象覆盖。在这种情况下,因为 access="none"用户只能浏览“销售”多维数据集,因为它是明确授予的。

<CubeGrant> 定义了对特定多维数据集的访问。至于<SchemaGrant>访问属性可以是“全部”,“自定义”或“无”,并可以覆盖多维数据集中的特定子对象。

<DimensionGrant> 定义对维度的访问。访问属性可以是“all”,“custrom”或“none”。“全部”的访问级别意味着该维度的所有子级别将获得继承的访问权限。“自定义”的访问级别意味着角色不能获得对子层次结构的固有访问权限,除非使用<HierarchyGrant>元素明确授予角色 。

<HierarchyGrant> 定义了对层次结构的访问。访问属性可以是“全部”,意味着所有成员都可见; “无”,意味着层级的存在对用户是隐藏的; 和“定制”。使用自定义访问权限,您可以使用该topLevel 属性来定义可见的最高级别(防止用户看到太多的“大图片”,例如查看收入卷起到 Store Country级别)。或者使用该bottomLevel 属性来定义可见的底层(这里,防止用户侵入个人客户的细节); 或者通过定义嵌套<MemberGrant> 元素来控制用户可以看到的成员集合。

你只能定义一个<MemberGrant> 元素,如果它包含<HierarchyGrant> 了access="custom"。会员赠与给予(或删除)给定会员及其所有子女的访问权限。这是规则:

  1. 成员继承父母的访问权限。如果你拒绝进入加州,你将无法看到旧金山。
  2. 赠款与订单有关。如果您准许进入美国,那么拒绝进入俄勒冈州,那么您将无法看到俄勒冈州或波特兰。但是,如果你拒绝进入俄勒冈州,然后准许进入美国,你可以有效地看到一切。
  3. 如果其中的任何一个成员可见,则该成员是可见的。假设您拒绝访问美国,然后授予访问加州。你将能够看到美国和加利福尼亚,但没有其他国家。然而,反对美国的总数仍将反映所有国家。如果父级HierarchyGrant指定最高级别,则只有等于或低于此级别的父级将可见。同样,如果指定了最低级别,则只有高于或等于该级别的子级才可见。
  4. 成员资助不会覆盖层级资助的顶级和底层级别。如果你设置topLevel="[Store].[Store State]",并授予访问加州,你将无法看到美国。会员授予不会覆盖topLevel和bottomLevel属性。您可以授予或拒绝访问任何级别的成员,但顶部和底部约束优先于显式成员授予。

在这个例子中,用户将可以访问加利福尼亚州以及加利福尼亚州除洛杉矶以外的所有城市。他们将能够看到美国(因为它的孩子,加利福尼亚,是可见的),但没有其他国家,而不是所有商店(因为它是高于顶层Store Country)。

9.2汇总政策 

一个汇总政策决定蒙德里安如何计算成员的总如果当前角色不能看到所有成员的孩子的。在名为“完整”的默认汇总策略下,该成员的总额包括来自不可见子项的贡献。例如,假设弗雷德属于可以看到角色[USA].[CA][USA].[OR],但不会[USA].[WA]。如果Fred运行查询

SELECT {[Measures].[Unit Sales]} ON COLUMNS,
    {[[Store].[USA], Store].[USA].Children} ON ROWS
FROM [Sales]

查询返回

[Customer][Measures].[Unit Sales]
[USA]266773
[USA].[CA]74748
[USA].[OR]67659

请注意[USA].[WA],根据访问控制策略,不会返回,但总数包括Fred无法看到的华盛顿总数(124,366)。对于某些应用,这是不合适的。特别是,如果该维度的成员数量较少,则最终用户可能能够推导出他们无权访问的成员的值。

为了解决这个问题,角色可以将不同的汇总策略应用于层次结构。该政策描述了如果当前角色只能看到该成员的一些子女,那么如何计算某个成员的总和:

  • 完整。该成员的总数包括所有的孩子。如果不指定rollupPolicy属性,则这是默认策略。
  • 部分。该成员的总数只包括无障碍儿童。
  • 隐藏。如果有任何一个孩子无法进入,那么总数是隐藏的。

根据“部分”政策,[USA]总数是无障碍儿童的总和,[CA] 并且[OR]

[Customer][Measures].[Unit Sales]
[USA]142407
[USA].[CA]74748
[USA].[OR]67659

在“隐藏”政策下,[USA]总计隐藏,因为其中一个子女无法访问:

[Customer][Measures].[Unit Sales]
[USA]-
[USA].[CA]74748
[USA].[OR]67659

策略是按照角色和层次来指定的。在以下示例中,角色查看[Store]层次结构的部分总计,但查看了 全部总计[Product]

<Role name="South Pacific manager">

<SchemaGrant access="none">

<CubeGrant cube="Sales" access="all">

<HierarchyGrant hierarchy="[Store]" access="custom" rollupPolicy="partial" topLevel="[Store].[Store Country]">

<MemberGrant member="[Store].[USA].[CA]" access="all"/>

<MemberGrant member="[Store].[USA].[CA].[Los Angeles]" access="none"/>

</HierarchyGrant>

<HierarchyGrant hierarchy="[Customers]" access="custom" rollupPolicy="full" topLevel="[Customers].[State Province]" bottomLevel="[Customers].[City]">

<MemberGrant member="[Customers].[USA].[CA]" access="all"/>

<MemberGrant member="[Customers].[USA].[CA].[Los Angeles]" access="none"/>

</HierarchyGrant>

<HierarchyGrant hierarchy="[Gender]" access="none"/>

</CubeGrant>

</SchemaGrant>

</Role>

此示例还显示了现有的功能,例如如何使用topLevel和/或bottomLevel属性来限制层次结构授予 ,以及如何使用access =“none”来防止角色看到层次结构。

9.3联盟角色 

联合角色结合了几个角色,并具有他们的特权总和。

联合角色可以看到特定的模式对象,如果其中一个或多个组成角色可以看到它。类似地,联合角色相对于特定层次结构的汇总策略是所有角色的汇总策略中限制最小的。

这里是一个显示联合角色的语法的例子。

<Role name="Coastal manager">

<Union>

<RoleUsage roleName="California manager"/>

<RoleUsage roleName="Eastern sales manager"/>

</Union>

</Role>

“加利福尼亚州经理”和“东部销售经理”的组成角色可能是常规角色,用户定义角色或联合角色,但必须在架构文件的较早版本中声明。“海岸经理”角色将能够看到任何成员或“加州经理”和“东方销售经理”。它将能够看到这些成员相交处的所有单元格,再加上它将能够看到两个角色都看不到的单元格:例如,如果只有“California manager”可以看到[USA].[CA].[Fresno],并且只有“Eastern sales manager”的[Sales Target]措施,则“滨海经理人”就能看到弗雷斯诺,既不构成角色的访问销售目标。

9.4设置连接的角色 

一个角色只有在与连接关联时才有效果。默认情况下,连接具有一个角色,使他们可以访问该连接架构中的每个多维数据集。

大多数数据库将角色(或“组”)与用户关联起来,并在用户登录时自动分配角色。但是,Mondrian不具有用户的概念,因此您必须以不同的方式来确定角色。有两种方法可以做到这一点:

  1. 在连接字符串中。如果Role 在连接字符串中指定关键字,连接将采用该角色。您可以指定用逗号分隔的多个角色名称,并创建一个联合角色; 如果角色名称包含逗号,请使用逗号将其转义。有关 连接字符串语法的示例,请参见 DriverManager类
  2. 以编程方式。一旦你的应用程序建立了连接,调用方法 Connection.setRole(Role)。您可以以编程方式创建角色(请参阅界面角色开发人员笔记链接以获取更多详细信息),或者使用 Schema.lookupRole(String)方法查看一个角色

10.附录A:XML元素 

元件描述

架构
<Schema>与特定企业有关的元素的顶层集合。立方体属于架构,共享维度,命名集合,角色,用户定义函数和参数。(维度和命名集合也可以在一个特定的Cube中被私人定义。)

物理元素
<PhysicalSchema>表格用法的集合,通过关系链接,构建逻辑模式。
<Table>定义一个表的使用。
<Query>使用SQL查询定义一个“表”,对于不同的底层数据库可以有不同的变体。
<InlineTable>使用内嵌数据集定义表格。
<Link>两个表之间的链接。
<ColumnDefs>持有人的<ColumnDef>元素。
<ColumnDef>定义<InlineTable>数据集中的列。
<ExpressionView>一个 or 的SQL表达式的集合,每个支持的方言的一个。<CalculatedColumnDef><Query>
<Column>参考以SQL表达(一内一列 元件),或在一个键结构体(, ,,, )。<SQL><Key><ForeignKey><Name><Caption><OrderBy>
<SQL>特定数据库方言的SQL表达式。
<Rows>持有人的元素。<Row>
<Row>在数据集中行。<InlineTable>
<Value>数据集中列的值。<InlineTable>

逻辑元素
<Cube>一个分析的主要业务视图,一个立方体是一个维度和度量的集合。
<Dimensions>收集要素。<Dimension>
<Dimension>一组可用于细分多维数据集的s集合,其中一些组织成 结构。<Attribute><Hierarchy>
<Attributes>收集要素。<Attribute>
<Attribute>数据项中的一个。<Dimension>
<Hierarchies>收集要素。<Hierarchy>
<Hierarchy>收集组织成。<Attributes><Levels>
<Levels>收集要素。<Level>
<Level>一级。<Hierarchy>
<Key>代替的关键字的列或列。<Attribute>Attribute.keyColumn
<Name>代替一个名称的列。<Attribute>Attribute.nameColumn
<Caption>代替的标题的列。<Attribute>Attribute.captionColumn
<OrderBy>列或用来的成员排序列,以代替。<Attribute>Attribute.orderByColumn
<Property>会员属性。定义是针对层次结构或层次的,但该属性将提供给所有成员。
<MeasureGroups>收集要素。<MeasureGroup>
<MeasureGroup>存储在同一个事实表中的s的集合。<Measure>
<Measures>收集要素。<Measure>
<Measure>测量。
<DimensionLinks>A 和S 之间的链接集合。<MeasureGroup><Dimension><Cube>
<ForeignKeyLink>使用事实表中的外键将a 链接到维表。<Dimension><MeasureGroup>
<FactLink>声明Dimension与MeasureGroup是平凡的链接,因为它的维度表与事实表相同。
<CopyLink>指示由MeasureGroup表中的键列表示的维。
<ReferenceLink>通过另一维将维度链接到MeasureGroup。
<NoLink>a 没有链接到当前的文档。<Dimension><MeasureGroup>
<CalculatedMembers>收集要素。<CalculatedMember>
<CalculatedMember>成员的值是使用公式派生的,定义为多维数据集的一部分。
<NamedSets>收集要素。<NamedSet>
<NamedSet>一个集合,其值是使用公式导出的,定义为一个立方体的一部分。
<Closure>将父子层次映射到闭包表。

访问控制
<Role>访问控制配置文件。
<SchemaGrant>一组模式的权限。
<CubeGrant>一组立方体的权利。
<HierarchyGrant>在该层次结构中对a 和s的一组权限。<Hierarchy><Level>
<MemberGrant>一套权利和一个孩子。<Member>
<Union>将一组权利定义为一组s 的联合。<Role>
<RoleUsage>对一个参考。<Role>

扩展
<UserDefinedFunction>声明一个用户定义的函数。
<CellFormatter>单元格式化程序。
<MemberFormatter>成员格式化程序。
<PropertyFormatter>属性格式化程序。
<Script>用于实现SPI的脚本片段,例如用户定义的函数,成员格式化程序或单元格格式化程序。

<Annotations>持有人的注释。
<Annotation>用户定义的属性附加到元数据元素。
<Parameter>部分层次的定义; 传递给MemberReader,如果存在的话。
<CalculatedMemberProperty>计算成员的属性。
<Formula>将公式文本保存在a <NamedSet><CalculatedMember>
<MeasureExpression>用于计算度量的SQL表达式来代替Measure.column

 

 

作者:Julian Hyde; 最后修改于2012年10月。
版权所有(C)2001-2005 Julian Hyde。
Copyright(C)2005-2013 Pentaho等

 

 

参考:https://github.com/Kyligence/kylin-mondrian

 

http://sourceforge.net/projects/mondrian/
http://mondrian.pentaho.com

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值