文章目录
-
- 1 什么是模式?
- 2 模式文件(Schema files)
-
- 2.1 注释
- 3 逻辑模型(Logical model)
-
- 3.1 多维数据集(cube)
- 3.2 度量(Measures)
- 3.3 维度(Dimensions),层次结构(Hierarchies),级别 (levels)
-
- 3.3.1 将维和层次映射到表上(Mapping dimensions and hierarchies onto tables)
- 3.3.2 'all'成员(The 'all' member)
- 3.3.3 时间维度(Time dimensions)
- 3.3.4 级别的顺序和显示(Order and display of levels)
- 3.3.5 多层次(Multiple hierarchies)
- 3.3.6 退化维度(Degenerate dimensions)
- 3.3.7 内联表(Inline tables)
- 3.3.8 成员属性和格式化程序(Member properties and formatters)
- 3.3.9 近似等级基数(Approximate level cardinality)
- 3.3.10 默认度量属性(Default Measure Attribute)
- 3.3.11 功能依赖性优化(Functional Dependency Optimizations)
- 3.3.12 表格提示(Table Hints)
- 3.4 属性顺序(Attribute order)
- 3.5 设计用于层次结构的属性(Designing attributes for use in hierarchies)
-
- 3.5.1 属性层次结构(Attribute hierarchies)
- 3.5.3属性与层次结构(Attributes versus Hierarchies)
- 3.6 架构捷径(Schema short cuts)
- 4 星型和雪花模式(Star and snowflake schemas)
-
- 4.1 共享维度(Shared dimensions)
- 4.2 连接优化(Join optimization)
- 5 高级逻辑构造(Advanced logical constructs)
-
- 5.1 虚拟多维数据集(Virtual cubes)
- 5.2 父子层次结构(Parent-child hierarchies)
-
- 5.2.1 调整父子层次结构(Tuning parent-child hierarchies)
- 5.2.2 封闭表(Closure tables)
- 5.2.3 填充封闭表(Populating closure tables)
- 5.3 成员属性(Member properties)
- 5.4 计算的成员(Calculated members)
- 5.5 命名集(Named sets)
- 6 插件(Plug-ins)
-
- 6.1 用户定义的功能(User-defined function)
- 6.2 成员读取器(Member reader)
- 6.3 单元阅读器(Cell reader)
- 6.4 单元格式化器(Cell formatter)
- 6.5 成员格式化程序员(Member formatter)
- 6.6 属性格式化程序(Property formatter)
- 6.7 动态模式处理器(Schema processor)
- 6.8 数据源更改监听器(Data source change listener)
- 6.9 动态数据源(Dynamic datasource xmla servlet)
- 7 国际化(Internationalization)
-
- 7.1 本地化模式处理器(Localizing schema processor)
- 8 聚合表(Aggregate tables)
- 9 访问控制(Access-control)
-
- 9.1 定义角色(Defining a role)
- 9.2 汇总政策(Rollup policy)
- 9.3 联盟角色(Union roles)
- 9.4 设置连接的角色(Setting a connection's role)
- 10 附录A:XML元素
1 什么是模式?
模式定义了一个多维数据库。它包含一个逻辑模型,由多维数据集,层次结构和成员组成,并将此模型映射到物理模型上。
逻辑模型由用于在MDX语言中编写查询的构造组成:多维数据集,维度,层次结构,级别和成员。
物理模型是通过逻辑模型呈现的数据的来源。它通常是一个星型模式,它是关系数据库中的一组表; 之后,我们将看到其他类型映射的例子。
2 模式文件(Schema files)
Mondrian模式用XML文件表示。
demo/FoodMart.xml是MOndrian分布式提供的包含我们在此讨论的几乎所有构造的示例模式。用于填充此模式的数据集也位于分布式中。
目前,创建模式的唯一方法是在文本编辑器中编辑模式XML文件。XML语法并不太复杂,所以这并不像听起来那么困难,特别是如果您使用FoodMart架构作为指导性示例。
XML文档的结构如下所示:
<Schema><Cube><Table><AggName>aggElements<AggPattern>aggElements<Dimension><Hierarchy>relation<Closure/><Level><KeyExpression><SQL/><NameExpression><SQL/><CaptionExpression><SQL/><OrdinalExpression><SQL/><ParentExpression><SQL/><Property><PropertyExpression><SQL/><DimensionUsage><Measure><MeasureExpression><SQL/><CalculatedMemberProperty/><CalculatedMember><Formula/><CalculatedMemberProperty/><NamedSet><Formula/><VirtualCube><CubeUsages><CubeUsage><VirtualCubeDimension><VirtualCubeMeasure><Role><SchemaGrant><CubeGrant><DimensionGrant><HierarchyGrant><MemberGrant/><Union><RoleUsage/><UserDefinedFunction/><Parameter/>relation ::=<Table><SQL/><View><SQL/><InlineTable><ColumnDefs><ColumnDef><Rows><Row><Value><Join>relationaggElement ::=<AggExclude><AggFactCount><AggIgnoreColumn><AggForeignKey><AggMeasure><AggLevel>
注意:XML元素的顺序很重要。例如,该UserDefinedFunction元件具有内发生Schema 的所有集合之后元件Cube, VirtualCube, NamedSet 和Role元件。如果您在第一个Cube元素之前包含它,模式的其余部分将被忽略。
每个XML元素的内容在附录A和XML模式中进行了描述 。
2.1 注释
主要元素类型(模式,多维数据集,虚拟多维数据集,共享维度,维度,层次结构,级别,度量,计算成员)支持注释。注释是一种将用户定义的属性与元数据元素相关联的方式,特别是,允许??工具添加元数据而不扩展Mondrian正式架构。
创建一个Annotations元素作为你希望注解的元素的子元素(通常它是第一个子元素,但检查模式定义的细节),然后包含一些Annotation元素。 Annotation元素的名称在元素中必须是唯一的。如果要添加注释以支持您维护的特定工具,请仔细选择注释名称,以确保它们不与其他工具使用的注释冲突。
以下示例显示附加到对象的“作者”和“日期”注释。
<Schema name="Rock Sales"><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 | 立方体 | 禁用多维数据集上的钻取链接 |
2.2 Schema元素
所述Schema是Mondrian Schema的根元素。例如:
<Schema name="Rock Sales" metamodelVersion="4.0">
Schema具有一个name属性,并且包含一个可选地description。其他属性在XML模式中描述,您可以通过附录A中的元素描述来访问它。(我们不会描述本指南中的每一个属性,只是最重要的属性,所以要养成点击链接的习惯!)
该metamodelVersion属性允许Mondrian识别Schema的目标版本; 如果版本不同于当前版本的软件,Mondrian可能会自动转换它。当前版本是“4.0”。如果该属性缺失(在Mondrian版本4之前它是可选的),Mondrian会尽量根据其内容推导出该模式的版本。
3 逻辑模型(Logical model)
逻辑模式中最重要的组件是多维数据集(cube),度量(measures)和维度(dimensions):
- 多维数据集是在某一领域中维度和度量的集合。
- 度量是你感兴趣的测量措施,例如,产品的单位销售或库存物品的成本价。
- 维度是一个属性,或者属性集,通过它可以将measures分割子类别。例如,您可能希望通过产品颜色,客户的性别以及销售产品的商店来给产品销量进行分类; 颜色,性别和商店都是dimensions。
我们来看一下简单模式的XML定义。
<Schema><Cube name="Sales"><Table name="sales_fact_1997"/><Dimension name="Gender" foreignKey="customer_id"><Hierarchy hasAll="true" allMemberName="All Genders" primaryKey="customer_id"><Table name="customer"/><Level name="Gender" column="gender" uniqueMembers="true"/></Hierarchy></Dimension><Dimension name="Time" foreignKey="time_id"><Hierarchy hasAll="false" primaryKey="time_id"><Table name="time_by_day"/><Level name="Year" column="the_year" type="Numeric" uniqueMembers="true"/><Level name="Quarter" column="quarter" uniqueMembers="false"/><Level name="Month" column="month_of_year" type="Numeric" uniqueMembers="false"/></Hierarchy></Dimension><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"/><CalculatedMember name="Profit" dimension="Measures" formula="[Measures].[Store Sales] - [Measures].[Store Cost]"><CalculatedMemberProperty name="FORMAT_STRING" value="$#,##0.00"/></CalculatedMember></Cube>
</Schema>
Schema包含一个名为“Sales”的cube。Sales cube有两个维度(dimensions):“Time”和“Gender”,以及四个度量单位(measures),即“Unit Sales”,“Store Sales”,“Store Cost”和“Profit”。
我们可以在这个模式上编写一个MDX查询:
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS,{descendants([Time].[1997].[Q1])} ON ROWS
FROM [Sales]
WHERE [Gender].[F]
此查询是指销售多维数据集([Sales]),每个维度 [Measures],[Time],[Gender],和这些维度中的各个成员。结果如下:
[Time] | [Measures].[Unit Sales] | [Measures].[Store Sales] |
---|---|---|
[1997].[Q1] | 0 | 0 |
[1997].[Q1].[Jan] | 0 | 0 |
[1997].[Q1].[Feb] | 0 | 0 |
[1997].[Q1].[Mar] | 0 | 0 |
现在我们来更详细地看一下模式定义。
3.1 多维数据集(cube)
立方体(请参阅Cube)是度量(measures)和维度(dimensions)的命名集合。measures和dimensions的共同之处就是事实表,例如"sales_fact_1997"。正如我们看到的,事实表的列保存度量计算的值,并且包含对保存维度的表的引用。
<Cube name="Sales">
<Table name="sales_fact_1997"/>
...
</Cube>
事实表是使用Table元素定义的。例如,如果事实表不在默认模式(schema)中,则可以使用“模式”属性提供显式模式,例如
<Table schema=" dmart" name="sales_fact_1997"/>
您也可以使用该 构造来构建更复杂的SQL语句。事实表不支持Join结构。
3.2 度量(Measures)
Sales cube定义了多个度量(measures),包括“Unit Sales”和“Store Sales”。
<Measure name="Unit Sales" column="unit_sales" aggregator="sum" datatype="Integer" formatString="#,###"/>
<Measure name="Store Sales" column="store_sales" aggregator="sum" datatype="Numeric" formatString="#,###.00"/>
每个度量(measures)(参见)都有一个名称,在事实表中的列名和一个 aggregator。aggregator通常是“sum”,但“count”,“min”,“max”,“avg”和“distinct-count”也是允许的; 如果您的cube包含父-子层次结构,则“distinct-count”有一些限制 。
可选属性datatype指定如何在Mondrian的缓存中表示单元值,以及如何通过XML将其返回给Analysis。datatype属性可以是“ String”,“ Integer”,“ Numeric”,“ Boolean”,“ Date”,“ Time”和“ Timestamp”等类型。默认值是“ Numeric”,除了“ count”和“ distinct-count”measures外,都是“ Integer”。
可选属性formatString指定如何打印值。在这里,我们选择输出没有小数位的单位销售额(Unit sales)(因为它是一个整数),并且存储小数点后两位的销售额(store sales)(因为它是货币值)。如果您使用意大利语运行, ‘,‘和’.’ 符号设置是敏感的,商店销售(store sales)可能显示为“48.123,45”。您可以使用高级格式字符串实现更多的wild效果。
measure可以通过Member.getCaption() 方法返回标题属性。如果要显示特殊字母(例如Σ或Π),则定义特定的标题是有意义的:
<Measure name="Sum X" column="sum_x" aggregator="sum" caption="Σ X"/>
measure可以使用cell reader,也可以使用SQL表达式来计算其值。“Promotion Sales”度量(measure)就是一个例子。
<Measure name="Promotion Sales" aggregator="sum" formatString="#,###.00"><MeasureExpression><SQL dialect="generic">(case when sales_fact_1997.promotion_id = 0 then 0 else sales_fact_1997.store_sales end) </SQL></MeasureExpression>
</Measure>
然后[Promotion Sales]根据它创建度量:
<Measure name="Promotion Sales" aggregator="sum" column="promotion_sales" formatString="#,###.00">
观察PhysicalSchema如何在一个地方完成工作并收集实施细节。度量定义不知道或关心promotion_sales列实际计算,并且看起来与在常规列上定义的度量相同。每次Mondrian需要访问计算列时,它都会生成SQL表达式。
在这种情况下,销售额只包括在总和中,如果它们对应于促销销售。可以使用任意的SQL表达式,包括子查询。但是,底层数据库必须能够在聚合上下文中支持该SQL表达式。通过在SQL标记中指定语法来处理不同数据库之间的语法差异。
<Attribute name="Quarter"><Key><Column name="the_year"/><Column name="quarter"/></Key>
</Attribute>
如果只有一个键列然后Key和keyColumn是等价的。使用你喜欢的任何一个:前者更统一,但后者打字更少。
我们不需要指定nameColumn,因为它默认为组合键中的最后一列。
如果维度表具有组合键,则该维度的键属性将具有组合键。为了从事实表中引用它,您需要在事实表MeasureGroup中创建ForeignKeyLink,它在事实表中为维表的复合主键的每个列使用一列。
为了提供单元格值的特定格式,度量可以使用单元格格式器(cell formatter) 。
3.3 维度(Dimensions),层次结构(Hierarchies),级别 (levels)
一些定义:
- member是由特定的一组属性值所确定的dimension的一个点。性别等级(gender hierarchy)有两个member:‘M’和’F’。‘San Francisco’, ‘California’ and 'USA’都是商店层级( store hierarchy)的member。
- hierarchy是为了便于分析的member集合。例如,商店结构(store hierarchy)由商店名称,城市,州和国家组成。该等级允许你形成中间小计:一个州的小计是该州所有城市的小计的总和,每个城市的小计是在该城市商店的小计总和。
- level是到层次结构根的距离相同的member集合。
- dimension是区分同一个事实表的属性的hierarchy的集合。
为了统一起见,measures被视为特殊维度的成员,称为“measures”。
一个例子
我们来看一个简单的维度。
<Dimension name="Gender" foreignKey="customer_id"><Hierarchy hasAll="true" primaryKey="customer_id"><Table name="customer"/><Level name="Gender" column="gender" uniqueMembers="true"/></Hierarchy>
</Dimension>
Dimension由单个Hierarchy组成,Hierarchy包含一个Gender层。(正如我们后面将会看到的那样,还有一个特殊的等级叫做 [(All)]总计。)
该维度(Dimension)的值来自customer表中的gender列。“gender”列包含两个值:‘F’和’M’,所以性别维度(Dimension)包含成员 [Gender].[F]和[Gender].[M]。
对于任何特定的销售,性别维度是购买过商品的客户的性别。这通过从事实表“sales_fact_1997.customer_id”连接到维度表“customer.customer_id”来表示。
3.3.1 将维和层次映射到表上(Mapping dimensions and hierarchies onto tables)
一个维度(dimension)通过一对列连接到一个cube,一个在事实表中,另一个在维度表中。该元件具有一个foreignKey属性,它是事实表中的列的名称; 该元素有一个primaryKey属性。
如果Hierarchy有多个表,则可以使用primaryKeyTable属性消除歧义。
column属性定义了级别的键(key)。它必须是级别表中列的名称。如果键(key)是表达式,则可以使用Level内的元素。以下内容等同于上述示例:
<Dimension name="Gender" foreignKey="customer_id"><Hierarchy hasAll="true" primaryKey="customer_id"><Table name="customer"/><Level name="Gender" column="gender" uniqueMembers="true"><KeyExpression><SQL dialect="generic">customer.gender</SQL></KeyExpression></Level></Hierarchy>
</Dimension>
其他Level,Measure和Property属性有相应的嵌套元素:
父元素 | 属性 | 等效的嵌套元素 | 描述 |
<Level> |
column |
<KeyExpression> |
级别的关键。 |
<Level> |
nameColumn |
<NameExpression> |
定义此级别成员名称的表达式。 如果未指定,则使用级别密钥。 |
<Level> |
ordinalColumn |
<OrdinalExpression> |
定义成员顺序的表达式。 如果未指定,则使用级别密钥。 |
<Level> |
captionColumn |
<CaptionExpression> |
形成成员标题的表达。 如果未指定,则使用级别名称。 |
<Level> |
parentColumn |
<ParentExpression> |
子成员在父子层次结构中引用其父成员的表达式。 未在常规层次结构中指定。 |
<Measure> |
column |
<MeasureExpression> |
SQL表达式来计算度量的值(SQL聚合函数的参数)。 |
<Property> |
column |
<PropertyExpression> |
SQL表达式来计算属性的值。 |
该highCardinality属性用于通知Mondrian这个维度中存在未定义和非常多的元素。可接受的值是true或false(最后一个是默认值)。当highCardinality="true"时,执行在在整个dimension元素集上的动作无法被执行。
3.3.2 'all’成员(The ‘all’ member)
默认情况下,每个hierarchy都包含称为’(All)’ 的level,它包含名为’(All {hierarchyName})’ 的单个member。此member是所有其他hierarchy的member的父亲,因此代表了总计。
它也是hierarchy的默认member; 也就是说,当hierarchy不包含在轴上或切片中时,该member用于计算单元格的值。在 allMemberName和allLevelName属性覆盖所有级别和所有成员的默认名称。
如果元素有 hasAll=“false”,'all’级别被抑制。该维度的默认成员将成为第一级的第一个成员; 例如,在时间层次结构中,它将是层次结构中的第一年。更改默认成员可能会造成混淆,因此您通常应该使用 hasAll=“true”。
该元素还有一个defaultMember属性,用于覆盖层次结构的默认成员:
<Dimension name="Time" type="TimeDimension" foreignKey="time_id">
<Hierarchy hasAll="false" primaryKey="time_id" defaultMember="[Time].[1997].[Q1].[1]"/>
...
3.3.3 时间维度(Time dimensions)
由于MDX时间相关的功能,基于年/月/周/日的时间维度在Mondrian模式中编码方式不同,例如:
- ParallelPeriod([level[, index[, member]]])
- PeriodsToDate([level[, member]])
- WTD([member])
- MTD([member])
- QTD([member])
- YTD([member])
- LastPeriod(index[, member])
时间维度有type=“TimeDimension”。在时间维度中角色的level由level的levelType属性表示,其可允许值如下所示:
levelType值 | 含义 |
---|---|
TimeYears | Level is a year |
TimeQuarters | Level is a quarter |
TimeMonths | Level is a month |
TimeWeeks | Level is a week |
TimeDays | Level represents days |
这是一个时间维度的例子:
<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.3.4 级别的顺序和显示(Order and display of levels)
请注意上面的时间层次结构示例中在元素上的ordinalColumn属性和nameColumn属性。这些会影响结果中的级别显示方式。该ordinalColumn属性指定Hierarchy表中的列,该列提供给定级别中的成员的顺序,而nameColumn指定将显示的列。
例如,在上面的月份级别中,datehierarchy表包含month(1…12)和month_name(January,February,…)列。MDX内部使用的列值是month列,因此有效的成员规格将采用以下格式:[Time].[2005].[Q1].[1]。该[Month]级别的成员将按照January, February等的顺序显示。
在父子层次结构中,成员总是按层次顺序排序。该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.5 多层次(Multiple hierarchies)
维度可以包含多个层次结构:
<Dimension name="Time" foreignKey="time_id">
<Hierarchy hasAll="false" primaryKey="time_id"><Table name="time_by_day" /><Level name="Year" column="the_year" type="Numeric" uniqueMembers="true" /><Level name="Quarter" column="quarter" uniqueMembers="false" /><Level name="Month" column="month_of_year" type="Numeric" uniqueMembers="false" />
</Hierarchy>
<Hierarchy name="Time Weekly" hasAll="false" primaryKey="time_id"><Table name="time_by_week" /><Level name="Year" column="the_year" type="Numeric" uniqueMembers="true" /><Level name="Week" column="week" uniqueMembers="false" /><Level name="Day" column="day_of_week" type="String" uniqueMembers="false" />
</Hierarchy>
</Dimension>
请注意,第一个层次结构没有名称。默认情况下,层次结构具有与其维度相同的名称,因此第一个层次结构称为“Time”。
这些层级没有太多共同之处 - 他们甚至没有相同的表!- 除了它们是从事实表中的同一列加入的,“time_id”。将两个层次结构放在同一维度的主要原因是因为它对最终用户更有意义:最终用户知道,在一个轴上具有“Time”层次结构,在另一个轴具有“Time Weekly”层次结构,这样是没有意义的。如果两个层次结构具有相同的维度,则MDX语言强制执行常识,并且不允许您在同一个查询中使用它们。
3.3.6 退化维度(Degenerate dimensions)
一个退化维度是一个不需要创建自己的维度表的维度。例如,请考虑以下事实表:
product_id | time_id | payment_method | customer_id | store_id | item_count | dollars |
---|---|---|---|---|---|---|
55 | 20040106 | 信用 | 123 | 22 | 3 | $ 3.54 |
78 | 20040106 | 现金 | 89 | 22 | 1 | $ 20.00 |
199 | 20040107 | 自动取款机 | 3 | 22 | 2 | $ 2.99 |
55 | 20040106 | 现金 | 122 | 22 | 1 | $ 1.18 |
假设我们为payment_method列中的值创建了一个维度表:
payment_method
- 信用
- 现金
- 自动取款机
这个维度表是相当无意义的。它只有3个值,不会添加额外的信息,并且会导致额外连接的成本。
相反,您可以创建一个退化维度。为此,请在没有表格的情况下声明一个维度,Mondrian将假定这些列来自事实表格。
<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>
请注意,因为没有连接,所以Dimension中的foreignKey属性不是必需的,并且该Hierarchy 元素没有
子元素或 primaryKey属性。3.3.7 内联表(Inline tables)
该构造允许您在模式文件中定义数据集。您必须声明列的名称,列类型(“字符串”或“数字”)和一组行。至于
和,您必须提供一个唯一的别名来引用数据集。这里是一个例子:
<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>
这与您在数据库中有一个名为’severity’的表具有相同的效果:
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值,请省略该 列,并且列的值将默认为NULL。
3.3.8 成员属性和格式化程序(Member properties and formatters)
正如我们稍后会看到的,一个level定义也可以定义member properties 和a member formatter。
3.3.9 近似等级基数(Approximate level cardinality)
level元素允许指定可选属性“approxRowCount”。通过减少确定级别,指定approxRowCount可以提高性能,层次结构和维度的基数。这在通过XMLA连接到Mondrian时会产生重大影响。
3.3.10 默认度量属性(Default Measure Attribute)
所述cube和VirtualCube元素允许指定可选属性“defaultMeasure”。
cube元素中的defaultMeasure允许用户显式地指定任何基本度量作为默认度量。
VirtualCube元素中的defaultMeasure允许用户显式地指定任何VirtualCube 度量作为默认度量。
请注意,如果未指定默认度量值,它将多维数据集中定义的第一个度量值作为默认度量值。在virtual cube的情况下,它将拾取在其中定义的第一个cube的第一个基本度量作为默认值。
在需要将计算成员选为默认度量的情况下,defaultMeasure指定显式会很有用。为了实现这一点,可以在其中一个基本立方体中定义计算成员,并将其指定为defaultMeasure虚拟立方体中的成员。
<Cube name="Sales" defaultMeasure="Unit Sales">
...
<CalculatedMember name="Profit" dimension="Measures"><Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>...
</CalculatedMember>
</Cube>
<VirtualCube name="Warehouse and Sales" defaultMeasure="Profit">
...
<VirtualCubeMeasure cubeName="Sales" name="[Measures].[Profit]" />
</VirtualCube>
3.3.11 功能依赖性优化(Functional Dependency Optimizations)
在某些情况下,可以通过利用正在处理的数据中的已知函数依赖性来优化性能。这种依赖性通常是与产生数据的系统相关联的业务规则的结果,并且通常不能仅通过查看数据本身来推断。
使用Property元素的dependsOnLevelValue属性和Hierarchy元素的uniqueKeyLevelName属性向Mondrian声明函数依赖关系。
property的dependsOnLevelValue属性用于指示该成员属性值依赖于在成员属性中定义的level值。换句话说,对于给定的level值,该属性的值是不变的。
Hierarchy的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的一部分,使用这些属性的任何模式都需要迁移到新语法。
3.3.12 表格提示(Table Hints)
Mondrian支持Table元素的一组有限数据库提示, 然后将其传递给涉及该表的SQL查询。这些提示如下:
数据库 | 提示类型 | 允许的值 | 描述 |
---|---|---|---|
MySQL | force_index | 此表上的索引名称 | 从该表中选择级别值时,强制使用指定的索引。 |
例如:
<Table name="automotive_dim">
<Hint type="force_index">my_index</Hint>
</Table>
与功能依赖优化一样,对表提示的支持处于过渡阶段,并且在Mondrian 4.0中可能会发生变化。作为升级到Mondrian 4.0的一部分,任何使用它们的模式都可能需要迁移到新的模式语法。
3.4 属性顺序(Attribute order)
属性的序号控制着成员的显示顺序。通常属性按名称排序。(如果未指定名称,请记住它将是组合键中的关键字或最后一列。)但有时名称并不能提供我们想要的顺序。该[Time].[Month]属性就是这样一个例子:
<Attribute name="Month"><Key><Column name="the_year"/><Column name="month"/></Key>
</Attribute>
就像[Time].[Quarter]月份有一个复合键。我们希望数据集涵盖每年12个月,而不仅仅是12个月。与Quarter不同,我们已经改写了这个名字。让我们看看如果我们执行一个查询会发生什么。
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.5 设计用于层次结构的属性(Designing attributes for use in hierarchies)
尽管从现有属性构建层次结构很容易,但您必须小心定义层次结构中包含的属性。每个属性必须在功能上依赖于它下面的级别的属性。因此,对于任何给定的Month,有且只有一个Quarter;对于给定的Quarter,只有一个Year。一个 Year-Month-Week-Day层次就通不过,因为第五周的一些天在一月,一些在二月。
通常,层次结构中的某些属性将具有组合键,以实现适当的功能依赖关系。记住在层次结构中包含属性不会改变该属性的成员数量是有用的。在格式良好的层次中,每个连续的层次都有更多的成员。例如,在Year-Quarter-Month-Day层级中超过10年以上,这些级别分别有10,40,120,3652个成员。如果你的Quarter属性只有4个成员(因为你忘记给它一个复合关键字),这个级别有10,4,120和3652个成员,而非递增顺序应该是你做错了事的标志。
3.5.1 属性层次结构(Attribute hierarchies)
令人惊讶的是,MDX语言不知道属性。它只知道维度,层次结构,级别和成员。 Mondrian利用这种手段来解决这个问题:它为每个属性生成一个单层次的层次结构,称为属性层次结构。
属性层次结构没有任何特殊属性。效果就像手动创建层次结构一样。但它为您节省了定义大量层次结构的工作量。最终效果是,在开始考虑层次结构之前,您可以轻松定义一打左右的属性并在查询中开始播放它们。
要控制属性是否具有层次结构,请使用Attribute元素的hasHierarchy属性。其他属性,大部分直接与Hierarchy元素的名称相似的属性直接对应,如下表所示:
层次结构属性 | Attribute属性 | 描述 |
---|---|---|
N / A | hasHierarchy | 属性是否具有属性层次结构。 |
名称 | N / A | 层次结构的名称。属性层次结构始终与该属性具有相同的名称。 |
hasAll | hierarchyHasAll | 层次结构是否具有“all”级别和成员。 |
allMemberName | hierarchyAllMemberName | 'all’成员的名称。如果未指定,则所有成员都被称为“All < hierarchyName >”。 |
allMemberCaption | hierarchyAllMemberCaption | 'all’成员的标题。如果未指定,则该成员的标题与其名称相同。 |
allLevelName | hierarchyAllLevelName | “all”级别的名称。如果未指定,则所有级别都称为“(all)”。 |
defaultMember | hierarchyDefaultMember | 层次结构的默认成员。如果未指定,则默认成员是第一级的第一个成员。(这是’all’成员,如果有的话)。 |
我们建议您在禁用属性层次结构的“all”成员或更改其默认成员之前考虑三次,因为即使该属性未显示在轴上,这也会导致隐藏的约束条件发挥作用。
3.5.3属性与层次结构(Attributes versus Hierarchies)
属性是Mondrian 4版本中的一项新功能。在之前的Mondrian版本中,您可以定义现在称为“属性层次结构(attribute hierarchies)”的内容,但您需要定义一些相当冗长的XML。另外,如果给定维度中有多个层次结构,则MDX语法很笨重。因此,模式设计人员通常会为时间和地理等“明显”层次维度定义层次结构,但不会将每个层次都公开为可用于独立切片和切片的属性。属性在大多数模式中是事后考虑的。
从Mondrian 4起,我们鼓励模式设计师设计具有多种属性的维度。首先不要担心创建层次结构。查看最终用户经常使用的属性组合,并考虑创建层次结构以使这些钻取路径更加方便。但我们期望您的最终用户在大部分时间内仍会使用独立属性。
一些属性具有“在父内”和“无父”形式。例如,该[Time].[Month]属性在10年内有120个成员,而[Time].[Month of Year] 只有12个成员。第一个可以用来比较2012年12月销售的平底雪橇是否比2011年12月销售更好; 第二个可以用来比较12月份的平底雪橇比4月更好。你需要定义两个独立的属性。没有简单的方法来依照一个定义另一个,或者它们之间的自动映射。你能做的最好是使用一个命名约定,如“ Attribute of Parent”,所有这些属性,这样的对应关系对你的最终用户来说都是清晰的。
3.6 架构捷径(Schema short cuts)
XML是用于定义模式的合适语言,因为它可以被人类和机器读取和写入。您可以用emacs,vi或notepad手写一个模式,或者您可以在建模工具中编写一个模式。但是,XML可能很冗长。这对于工具来说不是一个问题。简单的事情应该看起来很简单,而且不需要很多输入。
作为单例嵌套集合的简写属性(Attribute as shorthand for a singleton nested collection)
如果该集合只有一个元素的话,一个重复的简写是允许使用属性来代替嵌套元素的集合。
例如,如果您使用简单(非复合)键定义属性,则可以写入
<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 | 作为此度量参数的列。(当通过生成SQL表达式来实现此度量时,这些列将成为SQL聚合函的参数。) |
Table | keyColumn | Key | 形成此表的关键字的列。 |
Link | foreignKeyColumn | ForeignKey | 从该链接的引用表格到其引用的表格形成外键的列。 |
ForeignKeyLink | foreignKeyColumn | ForeignKey | 从度量值组的事实表到维度表形成外键的列。 |
继承的属性
table属性发生在Dimension,Attribute和Column元素,如果不存在,从封闭元素(enclosing element)中继承。这使得定义更加简洁,例如,一个维度基于一个单独表。
属性的默认值
许多属性都有默认值。例如,Dimension元素的type属性的默认值是“OTHER”。模式参考中描述了默认值。
共享维度
如果同一模式中的多个多维数据集使用具有相似定义的维度,请考虑定义共享维度。
4 星型和雪花模式(Star and snowflake schemas)
我们之前看到如何基于事实表,事实表(“Payment method”)和连接到事实表(“Gender”)的表的维度构建一个多维数据集。这是最常见的一种映射,称为星型模式。
但是,维度可以基于多个表格,前提是有一个明确定义的路径将这些表格连接到事实表。这种维度被称为雪花,并使用运算符定义。例如:
<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的右边组件(内部Join元素)由多个表组成。在这种情况下不需要leftAlias属性,因为leftKey列明确地来自"product"表。
4.1 共享维度(Shared dimensions)
为连接生成SQL时,Mondrian需要知道要加入的列。如果您要加入连接,那么您需要告诉它该列所属的连接中的哪些表(通常它将成为连接中的第一个表)。
由于共享维度不属于多维数据集,因此您必须为其提供一个明确的表格(或其他数据源)。当您在特定的多维数据集中使用它们时,可以指定外键。此示例显示使用sales_fact_1997.store_id外键将Store Type维度连接到Sales多维数据集,使用warehouse.warehouse_store_id外键将Store Type连接到Warehouse多维数据集:
<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>
4.2 连接优化(Join optimization)
模式中的表映射告诉Mondrian如何获??取数据,但是Mondrian足够聪明,不会逐字读取模式。它在生成查询时应用了许多优化:
- TODO:描述大尺寸的支持
- 如果一个维度(或者更确切地说,被访问的维度的级别)在事实表中,Mondrian不会执行连接。
- 如果两个维度通过相同的连接路径访问同一个表格,Mondrian只会将它们连接一次。例如, [Gender]和[Age]可能都在customers表的列中,通过加入sales_1997.cust_id = customers.cust_id。
5 高级逻辑构造(Advanced logical constructs)
5.1 虚拟多维数据集(Virtual cubes)
虚拟立方体组合了两个或更多常规立方体。它由 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>
CubeUsages元素是可选的。它指定了导入到虚拟多维数据集中的多维数据集拥有CubeUsage元素。
CubeUsage元素是可选的。它指定导入到虚拟多维数据集中的基本多维数据集。目前,可以定义VirtualCubeMeasure和从基础多维数据集类似的导入,而无需为多维数据集定义CubeUsage。CubeName属性指定正在导入的基础多维数据集。ignoreUnrelatedDimensions属性指定来自此基本多维数据集的度量将具有未加入维度的成员推送给顶级成员。此行为目前支持聚合。该属性默认为false。 ignoreUnrelatedDimensions是一个实验性特性,类似于2005年SSAS在类似命名的功能。 MSDN文档提及“当IgnoreUnrelatedDimensions为true时,不相关的维将被强制设置为最高级别;如果值为false,维度不会被强制设置为最高级别,此属性与多维表达式(MDX)ValidMeasure函数类似。目前Mondrian实施ignoreUnrelatedDimensions取决于使用ValidMeasure。例如,如果我们想要将此行为应用于“Warehouse and Sales”虚拟多维数据集中的“Unit Sales”度量,那么我们需要为上述示例中所示的“Sale”多维数据集定义CubeUsage条目,并使用ValidMeasure包装此度量。
VirtualCubeDimension元素从一个构成多维数据集中导入一个维度。如果您未指定cubeName属性,则表示您正在导入共享维度。(如果多维数据集中多次使用共享维度,目前无法消除您打算导入的共享维度的使用情况。)
VirtualCubeMeasure元素从其中一个构成多维数据集中导入一个度量。它是用相同的名称导入的。如果您想创建一个公式,或者只是在导入时重命名一个度量,请使用CalculatedMember元素。
virtual cubes出乎意料地在现实世界的得到频繁应用。它们发生在具有不同粒度的事实表(例如,一个度量在日期级别,另一个在月份级别)或具有不同维度的事实表(例如一个有产品,时间和客户,另一个有时间和仓库),并希望将结果呈现给不了解或关心数据结构的最终用户。
任何常见维度 - 两个构成多维数据集都使用的共享维度 - 会自动同步。在这个例子中,[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 父子层次结构(Parent-child hierarchies)
传统的等级制度有一套严格的等级制度,以及坚持这些制度等级的成员。例如,在Product层次结构中,Product Name 级别中的任何成员在Brand Name级别中都有父项,该Brand Name在Product Subcategory有父项,等等。这种结构有时过于僵化,无法模拟真实世界的数据。
一个父子层次结构只有一个级别(不包括特殊的“all”级别),但任何成员都可以在同一个水平有父母。一个典型的例子是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>
这里最重要的属性有parentColumn和nullParentValue:
- parentColumn属性是将成员链接到其父成员的列的名称; 在这种情况下,它是指向员工主管的外键列。ParentExpression子元素Level相当于parentColumn 属性,但允许你定义一个SQL表达式,就像Expression元素。parentColumn 属性(或ParentExpression元素)是有父子结构的hierarchy的Mondrian的唯一指示。
- nullParentValue属性是指示成员没有父项的值。缺省值是nullParentValue=“null”,但由于许多数据库不会对空值进行索引,因此模式设计器有时会将空值用空字符串、0和-1替换。
5.2.1 调整父子层次结构(Tuning parent-child hierarchies)
上面定义的父子层次结构存在一个严重的问题,那就是Mondrian为了计算单元总数所做的工作量。 假设雇员表包含以下数据:
employee
supervisor_id | employee_id | full_name |
---|---|---|
null | 1 | Frank |
1 | 2 | Bill |
2 | 3 | Eric |
1 | 4 | Jane |
3 | 5 | Mark |
2 | 6 | Carla |
如果我们要计算比尔的总薪资预算,我们需要增加Eric和Carla(向Bill报告)和Mark(向Eric报告)的薪水。 通常Mondrian会生成一个SQL GROUP BY 语句来计算这些总数,但是没有(通常可用的)可以遍历层次结构的SQL结构。 因此,默认情况下,Mondrian会为每位主管生成一条SQL语句,以检索并汇总该主管的所有直接报告。
这种方法有两个缺点。 首先,如果层次结构包含超过一百个成员,则表现不是很好。 其次,由于Mondrian通过生成SQL来实现distinct-count聚合器,因此无法在包含父子层次结构的任何多维数据集中定义distinct-count度量。
我们如何解决这些问题? 答案是增强数据,以便Mondrian能够使用标准SQL检索它需要的信息。 Mondrian支持一种称为 封闭表 的机制 。
5.2.2 封闭表(Closure tables)
封闭表是一个SQL表,其中包含每个员工/主管关系的记录,无论深度如何。 (用数学术语来说,这被称为员工/主管关系中的’自反传递闭包’。该 distance 列并不是严格要求的,但它使填充表更容易。)
employee_closure
supervisor_id | employee_id | distance |
---|---|---|
1 | 1 | 0 |
1 | 2 | 1 |
1 | 3 | 2 |
1 | 4 | 1 |
1 | 5 | 3 |
1 | 6 | 2 |
2 | 2 | 0 |
2 | 3 | 1 |
2 | 5 | 2 |
2 | 6 | 1 |
3 | 3 | 0 |
3 | 5 | 1 |
4 | 4 | 0 |
5 | 5 | 0 |
6 | 6 | 0 |
在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_id 和 employee_id NOT 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 填充封闭表(Populating closure tables)
每当层次结构发生变化时,表格都需要重新填充,这是应用程序的责任 - Mondrian不会这么做!
如果您正在使用Pentaho Data Integration (Kettle),则有一个特殊的步骤来将封闭表填入为ETL过程的一部分。 更多细节查看Pentaho Data Integration wiki。
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, distanceFROM 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, distanceFROM employee_closure, employeeWHERE employee_closure.employee_id = employee.supervisor_idAND employee_closure.distance = distance - 1;
UNTIL (ROW_COUNT() == 0))
END REPEAT;
END //
DELIMITER ;
5.3 成员属性(Member properties)
成员属性是由一个level中的Property元素定义的, 如下所示:
<Level name="MyLevel" column="LevelColumn" uniqueMembers="true">
<Property name="MyProp" column="PropColumn" formatter="com.example.MyPropertyFormatter"/>
<Level/>
formatter属性定义了一个property formatter,我们稍后进行解释。
在schema中的属性一旦定义, 您可以通过member.Properties(propertyName)函数在MDX语句中使用它们, 例如:
SELECT {[Store Sales]} ON COLUMNS,
TopCount(Filter([Store].[Store Name].Members,[Store].CurrentMember.Properties("Store Type") = "Supermarket"),10,[Store Sales]) ON ROWS
FROM [Sales]
如果可以的话,Mondrian会推断出属性表现的类型。 如果属性名是一个常量字符串,那么类型就基于属性定义的type属性(“String”, “Numeric” or “Boolean”)。 如果属性名是一个表达式 (例如 CurrentMember.Properties("Store " + “Type”)), Mondrian返回一个untyped值.
5.4 计算的成员(Calculated members)
假设您想要创建一个度量,其值不是来自事实表的列,而是来自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查询中,而是在schema中定义成员,作为多维数据集定义的一部分:
<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>
请注意, CalculatedMemberProperty(不是Property)元素对应于MDX语句的FORMAT_STRING = '$#,###'片段。 您也可以在这里定义其他属性,但FORMAT_STRING在实践中是最有用的。
FORMAT_STRING 属性值还可以使用表达式进行评价。 格式化特定单元格时,首先评估表达式以生成格式字符串,然后将格式字符串应用于单元格值。 这是与条件格式字符串相同的属性
<CalculatedMemberProperty name="FORMAT_STRING" expression="Iif(Value < 0, '|($#,##0.00)|style=red', '|$#,##0.00|style=green')"/>
有关格式字符串的更多详细信息,请参阅 MDX规范。
值得一提的另一个计算成员属性是DATATYPE。 与度量一样 ,设置数据类型指定如何通过用于分析的XML返回计算的成员。 计算成员的DATATYPE属性可以是“String”,“Integer”或“Numeric”类型:
<CalculatedMemberProperty name="DATATYPE" value="Numeric"/>
您可以为计算的成员属性指定SOLVE_ORDER。 解决顺序决定了竞争表达式中计算的优先级
<CalculatedMemberProperty name="SOLVE_ORDER" value="2000"/>
您可以使计算的成员或度量不可见。 如果您在Measure或CalculatedMember元素中指定visible=“false” (默认值为“true”),则JPivot等用户界面会注意到此属性并隐藏该成员。 如果您想在多个步骤中执行计算并隐藏最终用户的中间步骤,这非常有用。 例如,此处仅显示“Margin per Sqft”,并且其“Store Cost”,“Margin”和“Store Sqft”等因素是隐藏的:
<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 命名集(Named sets)
WITH SET子句的MDX语句允许您声明可在整个查询中使用的集合表达式。 例如:
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]
Warehouse | Warehouse Sales |
---|---|
Treehouse Distribution | 31,116.37 |
Jorge Garcia, Inc. | 30,743.77 |
Artesia Warehousing, Inc. | 29,207.96 |
Jorgensen Service Storage | 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 插件(Plug-ins)
有时,Mondrian的模式语言不够灵活,或者MDX语言不够强大,无法解决手头的问题。 你想要做的是在Mondrian应用程序中添加一些你自己的Java代码,而插件是一种方法。
每个Mondrian的扩展在技术上都是一个服务提供者接口(SPI); 简而言之,您编写代码实现的Java接口,Mondrian在运行时可以调用。 您还需要注册一个扩展(通常位于schema.xml文件中的某处),并确保它出现在类路径中。
插件包括用户定义的功能;单元格, 成员和属性格式化程序;动态模式处理器和数据源更改监听器。成员阅读器和单元阅读器不完全支持 ,并且将来我们可能会支持可插拔的SQL语言。
一些插件(用户定义的函数,成员格式化程序,属性格式化程序,单元格式化程序)可以用JavaScript等脚本语言实现。在这种情况下,您不需要编写Java类; 您只需将脚本代码封装在mondrian模式文件的Script元素中即可。 在脚本语言中实现的扩展通常不如在Java中实现的扩展,但是它们更方便,因为您不需要编译任何代码。只需修改mondrian模式文件中的脚本代码并重新加载模式即可。较短的代码调试-修复周期可以让您更快地开发应用程序。 一旦你在脚本中实现了插件,如果性能仍然是一个问题,你可以将你的插件翻译成Java。
其他扩展包括Dynamic datasource xmla servlet。
6.1 用户定义的功能(User-defined function)
用户定义的函数必须具有公共构造函数并实现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.2 成员读取器(Member reader)
成员读取器是访问成员的手段。层次结构通常基于维度表(星型模式的’臂’),因此使用SQL进行填充。但即使您的数据不在RDBMS中,也可以通过编写一个称为自定义成员读取器的Java类来使其显示为层次结构。
这里有几个例子:
- DateSource(待写)生成时间层次结构。传统上,数据仓库实现者为其系统可能处理的每个日期生成一个包含行的表。但问题是这个表需要加载,随着时间的推移,他们将不得不记得添加更多的行。DateSource在内存中按需生成日期成员。
- FileSystemSource(待写)将文件系统呈现为目录和文件的层次结构。由于一个目录可以有一个本身就是一个目录的父目录,所以它是一个父子层次结构。就像由DateSource创建的时间层次结构一样,这是一个虚拟层次结构:特定文件的成员仅在扩展该文件的父目录时才创建。
- ExpressionMemberReader (待写)根据表达式创建一个层次结构。
自定义成员读取器必须实现 mondrian.rolap.MemberSource接口。如果您需要为细粒度控制实施更大的一组成员操作,请实施派生的 mondrian.rolap.MemberReader接口; 否则,Mondrian会将您的阅读器包装在 mondrian.rolap.CacheMemberReader对象中。您的成员阅读器必须具有一个公共构造函数,该构造函数接受( RolapHierarchy, Properties)参数,并且不引发检查异常。
成员读取器使用Hierarchy元素的memberReaderClass属性声明; 任何Parameter子元素都通过properties构造函数参数传递。这里是一个例子:
<Dimension name="Has bought dairy">
<Hierarchy hasAll="true" memberReaderClass="mondrian.rolap.HasBoughtDairySource"><Level name="Has bought dairy" uniqueMembers="true"/><Parameter name="expression" value="not used"/>
</Hierarchy>
</Dimension>
6.3 单元阅读器(Cell reader)
尚未实施。语法会是类似的
<Measure name="name" cellReaderClass="com.example.MyCellReader"/>
和类“com.example.MyCellReader”将不得不实现mondrian.olap.CellReader接口。
6.4 单元格式化器(Cell formatter)
单元格格式化程序修改Cell.getFormattedValue()的行为。 该类必须实现mondrian.spi.CellFormatter接口,并且是这样指定的:
<Measure name="name">
<CellFormatter className="com.example.MyCellFormatter"/>
</Measure>
(以前的语法,使用Measure元素的’formatter’属性,已被弃用,并将在mondrian-4.0中被删除。)
您可以在JavaScript等脚本语言中使用Script元素指定格式化程序:
<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 MEMBER MDX查询 的 子句中 定义的计算度量 ,可以在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 formatter)
成员格式化程序修改的Member.getCaption()行为。 该类必须实现mondrian.spi.MemberFormatter接口,并且是这样指定的:
<Level name="name" column="column">
<MemberFormatter className="com.example.MyMemberFormatter"/>
</Level>
(以前的语法,使用Level元素的’formatter’属性已被弃用,并且将在mondrian-4.0中被删除。)
您可以在JavaScript等脚本语言中使用Script元素指定格式化程序:
<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 formatter)
属性格式化程序修改的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等脚本语言中使用Script元素指定格式化程序:
<Level name="name" column="column">
<Property name="MyProp" column="PropColumn"><PropertyFormatter><Script language="JavaScript">return member.getName().toUpperCase();</Script></PropertyFormatter>
</Property>
</Level>
该脚本有可用的member,propertyName和propertyValue变量,对应于所述的参数 mondrian.spi.PropertyFormatter.formatProperty(member构件,propertyName字符串,PropertyValue对象方法。 代码片段可以有多个语句,但必须以return语句结束 。
6.7 动态模式处理器(Schema processor)
动态模式处理器实现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.8 数据源更改监听器(Data source change listener)
数据源更改侦听器实现mondrian.spi.DataSourceChangeListener接口。 它被指定为连接字符串的一部分,如下所示:
Jdbc=jdbc:odbc:MondrianFoodMart; JdbcUser=ziggy; JdbcPassword=stardust; DataSourceChangeListener=com.example.MyChangeListener;
每当mondrian必须决定是否使用缓存中的数据时,它将调用更改监听器。 当更改监听器告诉mondrian数据源已经更改为维度,多维数据集…,mondrian将刷新缓存并再次从数据库读取数据。
在读取任何数据之前,应该在mondrian中调用此类,因此即使在构建缓存之前也是如此。这样,插件可以注册第一个时间戳mondrian尝试读取数据源。
每次启动查询时,都会检查聚合缓存是否已更改。 如果更改,缓存将被刷新,聚合将从数据源重新加载。
以下是数据源更改侦听器插件类的示例:
package com.example;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;import mondrian.olap.MondrianDef;
import mondrian.rolap.RolapHierarchy;
import mondrian.rolap.RolapUtil;
import mondrian.rolap.agg.Aggregation;
import mondrian.rolap.RolapStar;
import mondrian.spi.impl.DataSourceChangeListenerImpl;public class MyChangeListener extends DataSourceChangeListenerImpl {
public MyChangeListener() {
}public synchronized boolean isHierarchyChanged(RolapHierarchy hierarchy) {// Since this function is called many times, it is a// good idea to not check the database every time.// And use some sort of time interval...// Get name of the table (does not work if based on view)String tableName = getTableName(hierarchy);Connection jdbcConnection = null;DataSource dataSource =hierarchy.getRolapSchema().getInternalConnection().getDataSource();try {jdbcConnection = dataSource.getConnection();if (jdbcConnection != null) {// Check database whether hierarchy data source has changed// ...}}
}public synchronized boolean isAggregationChanged(Aggregation aggregation) {// The first time, register star and bitKey and remember first time of access...RolapStar star = aggregation.getStar();BitKey bitKey = aggregation.getConstrainedColumnsBitKey();// The first time this function is called, only the bitKey is set,// the columns are not filled up yet.RolapStar.Column[] columns = aggregation.getColumns();if (columns != null) {// Check database...}
}
}
6.9 动态数据源(Dynamic datasource xmla servlet)
DynamicDatasourceXmlaServlet延伸DefaultXmlaServlet,加入到动态加载datasources.xml文件。 对于它收到的每个客户端请求,它都会检查datasources.xml内容的更新。 它有选择地清除已更改或不再存在的datasources.xml目录的缓存 。 该servlet认为目录的任何属性更改都(DataSourceInfo,在DataSourcesConfig.Catalog中定义属性 )是不同的。它按名称识别目录。
这个servlet补充了基于 UseContentChecksum 的动态目录加载功能。 它不检查目录内容是否有更新。 功能没有重叠。 两者将共同提供全面的动态数据源和目录配置功能。
要使用DynamicDatasourceXmlaServlet,请在web.xml以下位置更改 MondrianXmlaServlet servlet的定义:
<servlet>
<servlet-name>MondrianXmlaServlet</servlet-name>
<servlet-class>mondrian.xmla.impl.DynamicDatasourceXmlaServlet</servlet-class>
...
</servlet>
这个实现有一个限制。 它要求目录名称在所有数据源中唯一,否则可能无法正常工作。
7 国际化(Internationalization)
国际化的Mondrian应用程序将为每种语言设计一个架构,每个对象的标题以当地语言显示。 例如, [Product] 维度的英文标题为“Product”,法文标题为“Produit”。
翻译模式对象的实际名称是不明智的,因为MDX语句也需要改变。 所有你需要改变的是标题。 每个模式对象(模式,多维数据集,虚拟多维数据集,维度,层次结构,级别,度量,命名集)都有一个标题属性,用户界面(如JPivot和Pentaho Analyzer)显示标题而不是真实名称。 另外:
- 每个模式对象都有一个描述属性。
- 层次结构可以具有allMemberCaption属性作为“all”成员的显示值。
- 对于模式,我们可以通过measuresCaption属性设置“度量”维度的显示值 。
- 如果成员是度量(即Measures维度的成员 ),则计算的成员具有属性CAPTION和DESCRIPTION,这些属性显示为标题和说明 。
创建国际化应用程序的一种方法是为每种语言创建模式文件的副本,但难以维护。 更好的方法是使用LocalizingDynamicSchemaProcessor 类对单个模式文件执行动态替换。
7.1 本地化模式处理器(Localizing schema processor)
首先,使用变量作为的模式中caption,description,allMemberCaption 和 measuresCaption属性的值,如下所示:
<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 Salesand# 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 聚合表(Aggregate tables)
当事实表包含大量行数:100万或更多时,聚合表格可以改善Mondrian的表现。 聚合表基本上是事实表中数据的预先计算的摘要。
我们来看一个简单的聚合表。
<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>
元素,这里没有显示,可以让你直接引用维度表,而不包括在聚合表中的列。 它在 聚合表指南中进行了描述。
在实践中,基于非常大的事实表的立方体可能有多个聚合表。 在模式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>
它告诉Mondrian将所有匹配模式的 “agg_.*_sales_fact_1997” 表格视为聚合表格,除了 “agg_c_14_sales_fact_1997” 和 “agg_lc_100_sales_fact_1997” 。 Mondrian使用规则来推断这些表中列的角色,因此遵守严格的命名约定很重要。命名约定在聚合表指南进行了描述 。
性能指南有关于选择聚合表 建议 。
9 访问控制(Access-control)
好的,现在你已经掌握了所有这些优秀的数据,但是你不是每个人都能够阅读所有这些。 为了解决这个问题,您可以将称为“角色”的访问控制配置文件定义为架构的一部分,并在建立连接时设置此角色。
9.1 定义角色(Defining a role)
角色由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属性可以是“all”或“none”; 这个访问可以被特定对象覆盖。在这种情况下,因为 access=“none” 用户只能浏览“Sales”多维数据集,因为它是明确授予的。
CubeGrant>定义了对特定多维数据集的访问权限。 至于SchemaGrant访问属性可以是“all”,“custom”或“none”,并且可以为多维数据集中的特定子对象覆盖。
DimensionGrant定义对维度的访问权限。 访问属性可以是“all”,“custrom”或“none”。 “all”的访问级别意味着该维度的所有子级别将获得继承访问权限。“custrom”的访问级别意味着角色不能获得对子层次结构的固有访问权限,除非使用HierarchyGrant元素明确授予该角色。
HierarchyGrant定义了对层次结构的访问。 访问属性可以是“all”,这意味着所有成员都可见; “none”,意味着层级的存在对用户是隐藏的; 和“custom”。 使用"custom"访问权限,您可以使用该topLevel属性来定义可见的最高级别(防止用户看到太多的“全貌”,例如查看收入卷起到 Store Country 级别); 或使用该bottomLevel属性来定义可见的底层(这里,防止用户侵入个人客户的细节); 或通过定义嵌套MemberGrant元素 来控制用户可以看到哪些成员集 。
您只能定义MemberGrant如果封闭元素HierarchyGrant有access=“custom”。 Member Grants给予(或移除)给定成员及其所有子女的访问权限。 这是规则:
- 成员继承父母的访问权限(Members inherit access from their parents)。 如果你拒绝进入加州(California),你将无法看到旧金山(San Francisco)。
- 赋权与订单有关(Grants are order-dependent)。 如果您准许进入美国(USA),然后拒绝访问俄勒冈州(Oregon),那么您将无法看到俄勒冈州(Oregon)或波特兰(Portland)。但如果你拒绝访问俄勒冈州(Oregon),然后授予访问美国(USA),你可以有效地看到一切。
- 如果其中的任何一个子成员可见,则该成员是可见的(A member is visible if any of its children are visible)。 假设你拒绝访问美国(USA),然后授予访问加利福尼亚州(California)。 你将能够看到美国(USA)和加利福尼亚州(California),但没有其他州。 然而,反对美国的总数仍将在all state状态下出现。 如果父级HierarchyGrant指定顶级,则只有等于或低于此级别的父级将可见。 同样,如果指定了最低级别,则只有高于或等于级别的子级才可见。
- 成员资助不会覆盖层次结构资助的顶级和底层级别(Member grants don’t override the hierarchy grant’s top- and bottom-levels)。 如果您设置 topLevel="[Store].[Store State]" 并授予加利福尼亚访问权限,您将无法看到美国。 会员授予不会覆盖topLevel和bottomLevel属性。 您可以授予或拒绝对任何级别的成员的访问,但顶部和底部约束优先于显式成员授予。
在这个例子中,用户将可以访问加利福尼亚州(California)以及加利福尼亚州除洛杉矶(Angeles)以外的所有城市。 他们将能够看到美国(因为它的孩子,加利福尼亚州是可见的),但没有其他国家,也没有所有商店(因为它在顶层以上Store Country )。
9.2 汇总政策(Rollup policy)
一个汇总政策决定如果当前角色不能看到所有成员的孩子的子成员,mondrian如何计算成员的总和。在名为’full’的默认汇总策略下,该成员的总额包括来自子项的不可见的贡献。 例如,假设Fred属于可以看到[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)。 对于某些应用程序,这不合适。 特别是,如果维度的成员数量较少,最终用户可能能够推断出他们无权访问的成员的值。
为了解决这个问题,角色可以将不同的汇总策略应用于层次结构。 该政策描述了如果当前角色只能看到该成员的一些子女,则如何为特定成员计算总和:
- Full。 该成员的总数包括所有的孩子。 如果您未指定rollupPolicy属性, 则这是默认策略。
- Partial。 该成员的总数只包括可访问的子成员。
- Hidden。 如果有任何一个子成员是不可访问的,则总计是隐藏的。
根据“Partial”政策, [USA]总数是可访问子成员的总和,[CA]和[OR] :
[Customer] | [Measures].[Unit Sales] |
---|---|
[USA] | 142407 |
[USA].[CA] | 74748 |
[USA].[OR] | 67659 |
在“Hidden”政策下,[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 联盟角色(Union roles)
联盟角色结合了多个角色,并拥有他们的特权总和。
如果一个联合角色的一个或多个组成角色可以看到它,联合角色可以看到特定的模式对象。 类似地,联合角色相对于特定层次结构的汇总策略对所有角色的汇总策略的限制最小。
这里是一个显示联合角色语法的例子。
<Role name="Coastal manager">
<Union><RoleUsage roleName="California manager"/><RoleUsage roleName="Eastern sales manager"/>
</Union>
</Role>
角色“California manager”和“Eastern sales manager”可能是常规角色,用户定义角色或联合角色,但他们必须在架构文件的较早版本中声明。 “Coastal manager”角色将能够看到任何成员或“California manager”和“Eastern sales manager”。 它将能够看到这些成员相交处的所有单元格,再加上它将能够看到两个角色都看不到的单元格:例如,如果只有“California manager”可以看到 [USA].[CA].[Fresno] ,并且只有“Eastern sales manager”[Sales Target]度量,那么“Coastal manager”将能够看到Fresno的销售目标,这两个组成角色都无法访问。
9.4 设置连接的角色(Setting a connection’s role)
角色只有在与连接关联时才有效。 默认情况下,连接具有一个角色,使他们可以访问该连接架构中的每个多维数据集。
大多数数据库将角色(或“组”)与用户关联起来,并在用户登录时自动分配角色。但是,Mondrian没有用户的概念,因此您必须以不同的方式建立角色。 有两种方法可以做到这一点:
- 在连接字符串中(In the connect string)。 如果您在连接字符串中指定Role关键字,则连接将采用该角色。 您可以指定用逗号分隔的多个角色名称,并创建一个联合角色; 如果角色名称包含逗号,请使用逗号将其转义。 有关连接字符串语法的示例, 请参见 DriverManager类 。
- 以编程方式(Programmatically)。 一旦您的应用程序建立了连接,请调用 Connection.setRole(Role)方法 。 您可以通过编程方式创建角色(有关更多详细信息, 请参阅 界面角色和 开发人员笔记链接 ),或使用 Schema.lookupRole(String)方法查看一个角色 。
10 附录A:XML元素
元素 | 描述 |
<Schema> |
多维数据集,虚拟多维数据集,共享维度和角色的集合。 |
逻辑元素 |
|
<Cube> |
维度和度量的集合,都集中在一个事实表上。 |
<VirtualCube> |
通过结合一个或多个多维数据集的维度和度量而定义的多维数据集。源自另一个多维数据集的度量可以是一个 <CalculatedMember> 。 |
<CubeUsages> |
导入到虚拟多维数据集中的基本多维数据集 |
<CubeUsage> |
虚拟多维数据集中基础多维数据集的使用。 |
<VirtualCubeDimension> |
虚拟多维数据集中维度的使用。 |
<VirtualCubeMeasure> |
虚拟多维数据集度量的使用。 |
<Dimension> |
维度。 |
<DimensionUsage> |
多维数据集共享维度的使用。 |
<Hierarchy> |
层次结构。 |
<Level> |
层次结构的级别。 |
<KeyExpression> |
用作关键级别的SQL表达式,代替列。 |
<NameExpression> |
用于计算成员名称的SQL表达式,代替 Level.nameColumn 。 |
<CaptionExpression> |
用于计算成员的标题的SQL表达式,代替 Level.captionColumn 。 |
<OrdinalExpression> |
用于同级别的成员排序的SQL表达式,代替 Level.ordinalColumn 。 |
<ParentExpression> |
用于计算度量的SQL表达式,代替 Level.parentColumn 。 |
<Property> |
成员属性。该定义是针对层级或级别的,但该属性将提供给所有成员。 |
<PropertyExpression> |
用于计算属性值的SQL表达式,代替 Property.column 。 |
<Measure> |
度量。 |
<CalculatedMember> |
成员的值是使用公式派生的,定义为多维数据集的一部分。 |
<NamedSet> |
集合的值是使用公式导出的,定义为一个多维数据集一部分。 |
物理元素 |
|
<PhysicalSchema> |
表格用法集合,通过关系链接,构建逻辑模式。 |
<Table> |
事实或维度表。 |
<View> |
使用SQL查询定义一个“表”,对于不同的底层数据库可以有不同的变量。 |
<Join> |
通过加入一组查询来定义一个“表”。 |
<InlineTable> |
使用内嵌数据集定义表。 |
<Closure> |
将父子层次映射到封闭表。 |
聚合表 |
|
<AggExclude> |
按名称或模式匹配排除候选聚合表。 |
<AggName> |
声明一个通过名称匹配的聚合表。 |
<AggPattern> |
通过正则表达式模式声明一组聚合表。 |
<AggFactCount> |
指定候选聚合表中包含事实表行数的列的名称。 |
<AggIgnoreColumn> |
让Mondrian忽略聚合表中的一列。 |
<AggForeignKey> |
将事实表中的外键映射到候选聚合表中的外键列。 |
<AggMeasure> |
将度量映射到候选聚合表中的列。 |
<AggLevel> |
将级别映射到候选聚合表中的列。 |
访问控制 |
|
<Role> |
访问控制配置文件。 |
<SchemaGrant> |
一个模式权限的集合。 |
<CubeGrant> |
一个多维数据集权限的集合。 |
<HierarchyGrant> |
一个层次结构和该层次结构中的级别的权限的集合。 |
<MemberGrant> |
一个成员及其子成员的权限的集合。 |
<Union> |
将权利集合定义为角色集合的联合。 |
<RoleUsage> |
对角色的引用。 |
扩展 |
|
<UserDefinedFunction> |
声明一个用户自定义的函数。 |
<CellFormatter> |
单元格格式化程序。 |
<MemberFormatter> |
成员格式化程序。 |
<PropertyFormatter> |
属性格式化程序。 |
<Script> |
用于实现SPI的脚本片段,例如用户定义的函数,成员格式化程序或单元格格式化程序。 |
其它 |
|
<Annotations> |
持有人注释。 |
<Annotation> |
用户定义的属性附加到元数据元素。 |
<Parameter> |
层次结构定义的一部分; 如果存在的话,传递给MemberReader。 |
<CalculatedMemberProperty> |
计算成员的属性。 |
<Formula> |
将公式文本保存在a <NamedSet> 或 <CalculatedMember> 。 |
<ColumnDefs> |
<ColumnDef> 元素的持有人。 |
<ColumnDef> | <InlineTable> 数据集中列的定义。 |
<Rows> |
<Row> 元素的持有人。 |
<Row> |
在<InlineTable> 数据集中的行。 |
<Value> |
<InlineTable> 数据集中列的值。 |
<MeasureExpression> |
用于计算度量的SQL表达式,代替 Measure.column 。 |
<SQL> |
特定数据库语言的SQL表达式。 |
其他参考链接:https://mondrian.pentaho.com/schema.html#XML_Query