如何设计 Mondrian Schema模式
1.什么是Schema
2.Schema模式文件
1.Annotation注解
3.逻辑模型
1.Cube数据立方
2.Measure数据度量
3.Dimesions,Hierarchies,Levels:维度,层次,层级
1.维度,层次映射到数据库表结构中
2.“All”成员
3.时间维度
4.Level的摆放顺序与显示效果
5.多个层次
6.维度退化
7.Inline表
8.Member成员的属性与格式
9.近似的level层级基数
10.缺省的Measure度量属性
11.有效的依赖优化
12.table表格注释
4.星型模式与雪花模式
1.共享的维度
2.Join连接优化
5.高级逻辑结构设计
1.虚拟数据立方
2.父-子层次
1.调整父-子层级
2.闭合表
3.填充闭合表
3.Member成员属性
4.计算的成员
5.命名集合
6.插件
1.用户自定义函数
2.Member成员读取器
3.Cell单元格读取器
4.Cell单元格式
5.Member成员格式
6.属性格式
7.Schema模式处理器
8.数据源更新监听器
9.动态数据源xmla servlet
7.国际化
8.聚合表
9.Access-control控制取值
1.定义角色
2.汇总策略
3.角色联合
4.设置连接器角色
10.附录A:XML元素
1.什么是模式Schema
一个模式文件定义了一个多维数据库。模式中包含了一个逻辑模型,逻辑模型
由数据立方Cube,层次Hierarchies,成员members,以及一个将逻辑模型与物理模型的映射组成。
逻辑模型包含了将在MDX语言的查询中要使用到的结构:cubes,dimensions,hierachies,levels,以及members.
物理模型是数据源,物理模型通过逻辑模型来呈现。物理模型是一个典型的星型模式,即是关系数据库中的一系列表。后面,我们将展示一个其他类型的映射(雪花型)。
2.模式文件
Mondrian模式文件是一个xml文件。Mondrian在其下载包的“demo/FoodMart.xml”中,提供了一个模式文件的例子,包含了几乎所有我们会讨论到的结构。运行这个模式的数据集也在发布的下载包中。
当前,创建模式文件的唯一方法是在文本编辑器中编辑一个模式的xml文件。XML的语法并不复杂,因此,当你在使用FoodMart.xml模式文件作为一个引导实例时,并不困难。
XML文件的结构如下:
aggElements aggElements relation relation ::=
|
relation aggElement ::=
注意:XML元素在文件中的放置顺序很重要,比如
元素必须出现在 ,元素里面,且在所有 , 和 的集合的后面。
每一个XML元素的内容都在附录和XML模式中进行了描述。
2.1 Annotation注解
模式文件中的主要元素类型(Schema,Cube,Virtual cube,Shared dimesion,Dimesion,Hierarchy,Level,Measure,Calculated member)都支持注解。注解是一种将用户自定义属性与元数据联系起来的方式,另外,注解还允许工具在不扩展官方Mondrian模式的前提下添加元数据。
创建一个
ons>元素作为一个你希望注解的元素的child(通常是第一个child元素,可以查看schema定义),然后包含一些 on>元素,这些元素的名字必须在他们的父元素中是唯一的。如果你打算添加annotations来支持一个你使用的工具,那么要谨慎选择annotation的命名,以确保不会跟其他工具使用的annotations冲突。
下面的例子展现了Author和Date元素的注解
Fred Flintstone 10,000 BC …
3.逻辑模型Logical model
Schema模式文件中最重要的组成部分是:数据立方Cube,度量Measure,维度Dimession.
数据立方Cube:是一个在特定对象区域中的维度和度量的集合。
度量Measure:是一个你希望度量计算的数量值,比如产品的销售数,或产品的成本。
维度Dimession:是一个属性,或者一个属性集合,通过维度,可以将度量划分到不同的子类别中。比如,你希望对Sales表根据颜色,客户性别和商品所在门店进行划分的话,那么颜色,性别和门店就是不同的维度。
让我们来看一看一个XML定义的简单schema例子。
这个schema文件包含了一个简单的叫做“Sales”的数据立方Cube,这个cube有两个维度:时间和性别,还有4个度量:单元销售,门店销售,门店成本,利润。
我们可以根据这个模式文件来编辑MDX查询语句:
SELECT {[Measures].[Unit Sales], [Measures].[Store Sales]} ON COLUMNS, {descendants([Time].[1997].[Q1])} ON ROWS FROM [Sales] WHERE [Gender].[F]
这条查询语句使用了Sales 数据立方[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
接下来,我们来了解schema模式的更多细节。
3.1 数据立方Cube
数据立方Cube包含了度量和维度在其内。度量与维度处于同一个事实表中,这个例子中的事实表是sales_fact_1997。正如我们将看到的,事实表包含了用于计算度量的列,并且包含了拥有维度的数据库表的参照。
...
事实表使用
元素来定义。如果事实表不在默认的Schema模式中,我们可以通过Schema的属性明确提供一个模式,比如:
< Table schema=" dmart" name=“sales_fact_1997"/>
我们也可以使用
结构来构建更复杂的SQL语句。 结构不支持事实表。
3.2 度量Measure
Sales数据立方中定义了若干个度量,包括“Unit Sales”和”Store Sales”。
每个度量都有一个名字,以及一个对应事实表中的列,和一个aggregator。
aggregator通常包括:sum,count,min,max,avg,distinct-count。其中distinct-count在使用包含父-子层级的数据立方时存在一些限制条件。
可选的属性datatype指定了在Mondrian的缓存中cell的值以何种格式展示,以及通过xml文件如何返回分析查询结果。datatype属性包含的值包括:String,Integer,Numeric,Boolean,Date,Time和Timestamp。默认的是Numeric。而count和distinct-count属性的默认格式是Integer。
一个可选的formatString属性指定了这个度量值是如何呈现(打印)出来的。
一个度量可以拥有一个标题属性caption来代替name属性,并通过Member.getCaption()方法返回其标题。定义一个指定的标题有时很有作用,比如 使用特殊字符(如Σ 或 Π)展示时。
< Measure name="Sum X" column="sum_x" aggregator="sum" caption="Σ X”/>
度量可以不是来自于某个列,它可以使用一个cell reader,或一个度量可以使用一个SQL表达式来计算它的值。比如度量“Promotion Sales”就是这样一个例子:
(case when sales_fact_1997.promotion_id = 0 then 0 else sales_fact_1997.store_sales end)
这个例子中,如果sales属于一个推广销售,那么sales将包含进总和里面。任意的SQL表达式可以使用,当然也包括子查询。然而,底层的数据库必须能够支持将SQL表达式写进一个aggregate聚合环境中。不同数据库之间的语句变化将通过指定的SQL标签中的dialect方言进行处理。
一个度量可以使用cell formatter来提供一个cell 值的具体格式。
3.3 维度,层次,层级
更多的定义:
(1)成员,指的是在一个维度内,由属性值的一个特定集合指定的一个取值点。比如 性别层次用两个成员:M和F。商店层次有三个成员:’San Francisco', 'California' 和 'USA' 。(2)层次,指的是一个成员的集合。为了方便分析,成员的集合被组织进一个结构中。比如,商店层次包括店名,城市,州和国家。这个层次允许我们构造一个中间的部分整体。对于一个state州来说,这个部分整体就是这个州内的所有城市的部分整体的总和,每个城市中的部分整体又是这个城市中所有商店的部分整体的总和。
(3)层级,指的是所有到顶层root层级相同距离的成员的集合。
(4)维度,指的是层次的集合。这些层次通过相同的事实表属性进行区分。
为了统一,度量作为一种特殊的维度,称之为Measures维度。
下面是一个简单的维度的例子:
这个维度包含了一个单独的层次,其中包含的层级由一个叫Gender的层级组成。(我们以后将会介绍,这里还有一个特殊的层级:All,包含了一个总计值)
这个维度的值来自于customer表的gender列。gender列包含了两个值:F和M,因此Gender维度包含了两个成员:[Gender].[F]和[Gender].[M]。
对于任意给定的sale,gender维度指的是进行支付的顾客的性别。这是通过join事实表sales_fact_1997.customer_id和维度表customer.customer_id来表现的。
3.3.1 维度、层次与数据库表的映射
维度与cube数据立方建立连接,是通过一对“列字段”来实现的,其中一个列是在事实表中,另一个列是在维度表中。
元素有一个foreignKey属性,它是事实表中的对应的某个列的名字; 元素有一个primaryKey属性。 如果层次中的数据表有一个以上,我们可以使用primaryKeyTable属性来指定主表。
column属性定义了这个层级上的key值。这个属性必须是这个层级数据库表中的一个列的名字。如果这个key值是个表达式,我们可以在Level内部使用
元素来代替。下面是一个等同于上面的一个例子:
customer.gender
关于
, , 元素的其他属性对应了嵌套的元素: Parent element Attribute Equivalent nested element Description
column Key of level.
nameColumn Expression which defines the name of members of this level. If not specified, the level key is used.
ordinalColumn Expression which defines the order of members. If not specified, the level key is used.
captionColumn Expression which forms the caption of members. If not specified, the level name is used.
parentColumn Expression by which child members reference their parent member in a parent-child hierarchy. Not specified in a regular hierarchy.
column SQL expression to calculate the value of the measure (the argument to the SQL aggregate function).
column SQL expression to calculate the value of the property.
uniqueMembers属性也是用于优化SQL语句生成。如果我们知道维度表中给定的层级level列的值在父层级levels的该列值中是唯一的,就设uniqueMembers=true,否则就是false。比如,类似于[Year].[Month]的时间维度在Month层级的uniqueMembers=false,因为不同的years中会出现相同的Month。另一方面,如果我们有一个层次[product class].[product name],并且确定[product name]是唯一的,那么就可以设uniqueMembers=true。如果不确定是不是唯一的,那么通常设置为false。在顶层的level层级中,通常是为true,因为没有更高的父层级了。
highCardinality属性用于通知Mondrian,在当前dimesion维度中有未定义的,且非常大的元素。这个属性的值是true或false(默认)。当设置highCardinality=true时,执行在所有维度元素集上到动作将不能在执行了。
3.3.2 ‘all’成员
缺省情况下,每个hierarchy层次包含一个顶级层级:All,这个顶级层级包含一个单一成员:’(All {hierarchyName})’。’all’成员是hierarchy层次所有其他成员的父级,并且代表一个累计值。这个成员也是hierarchy的默认成员;也就是说,当这个层次没有包含在一个轴或切片上时,这个’all’成员用于计算单元格的值。allMemberName 和 allLevelName属性覆盖了所有层级和成员的缺省名字。
如果在
元素中设置了 hasAll=“false”,那么’all’层级就失效了。这个维度中的默认成员将会成为第一个level层级的第一个成员,举个例子,在Time层次中,将会是这个层次的第一年。改变默认成员将会被不允许,因此通常使用 hasAll=“true”.
元素也有一个defaultMember属性,以覆盖层次中的默认成员: ...
3.3.3 时间维度
基于year/month/week/day的时间维度在编码上比较特殊,因为MDX中有与时间相关的函数,比如:
ParallelPeriod([level[, index[, member]]])
PeriodsToDate([level[, member]])
WTD([member])
MTD([member])
QTD([member])
YTD([member])
LastPeriod(index[, member])
时间维度有一个属性:type=“TimeDimension”。时间维度中的层级的角色通过层级的属性levelType来
体现,这个属性可以取如下的值:
levelType valueMeaning
TimeYearsLevel is a year
TimeQuartersLevel is a quarter
TimeMonthsLevel is a month
TimeWeeksLevel is a week
TimeDaysLevel represents days
下面是一个关于时间维度的例子:
3.3.4 level层级的出现顺序与显示效果
注意上述关于时间层次的例子中的
元素中的ordinalColumn 和 nameColumn属性,这些属性影响层级在结果中如何显示。ordinalColumn属性指明层次表中的一个列,这个列提供 指定层级中成员的顺序。nameColumn指明将显示的列。
比如,在上述Month层级中,datehierarchy层级表有month(1,…,12)和month_name(January, February, …)列。在MDX内部将被使用的列的值就是month列,因此有效的成员定义的形式是:[Time].[2005].[Q1].[1]。[Month]层级的成员将按照月份先后顺序进行呈现。
在一个父子层次中,成员都按照层次顺序进行了排序。ordinalColumn属性控制父层次中的兄弟层次的显示顺序。
序数列可以是任意的数据类型,即能用于order by的语句中。因此,在上述例子中,day_in_month列将会在每个月中循环呈现。从JDBC驱动中返回的值需为非空的java.lang.Comparable实例,当Comparable.compareTo方法被调用时,产生期望的有序结果。
Level层级包含一个type属性,可取值:"String", "Integer", "Numeric", "Boolean", "Date", “Time”和”Timestamp”。默认是Numeric,因为主键列的值通常为Numeric类型。如果是不同的类型,Mondrian需要知道正确类型,以产生正确的SQL语句。比如,字符串将会用单引号生成:WHERE productSku = ‘123-455-AA’;
3.3.5多个层次
一个维度中可以包含多个层次:
要注意的是,第一个层次没有指定名称,默认情况下,层次拥有跟其所属的维度一样的名称,因此第一个层次也叫做“Time”。
除了通过事实表中一个相同用于表连接的列 “time_id”之外,这些层次没有太多相同的地方,他们甚至连层次表都不同。将两个层次放在同一个维度中的主要原因是:这样做对于最终用户更有意义,最终用户知道,如果将“Time”层次放在一个轴上,而“Time Weekly”层次放在其他轴上,将毫无疑义。如果两个层次在相同维度下,那么MDX语言将不允许你在一个查询中同时使用这两个层次。
3.3.6 退化的维度
所谓 退化的维度,指的是一个维度太简单,以至于都没有必要为它创建一个维度表。举个例子,看看如下的事实表:
product_id time_id payment_method customer_id store_id item_count dollars
55 20040106 Credit 123 22 3 $3.54
78 20040106 Cash 89 22 1 $20.00
假设我们为payment_method列上的值创建一个维度表,这个维度表毫无意义,反而会导致额外的表连接开销。
如果我们使用一个退化的维度,就可以更简单。首先声明一个不包含表的维度,Mondrian将假定这些列来自于事实表。
注意,由于此处不存在表联接了,所以维度的foreignKey属性也不需要了,而且维度中的Hierarchy层次元素也不需要
子元素和primaryKey属性了。
3.3.7 内联表
内联表结构允许我们在Schema模式文件中定义一个数据集。我们需要声明这些列的名字,类型,以及行的集合。为了在 和
元素中使用,还必须为这个数据集定义一个唯一的名字。下面是一个例子: |
1 High |
2 Medium |
3 Low
定义一个名叫severity的内联表的效果,其实等同于你在数据库中存在一个名叫“severity”的表:
如表结构和值:
iddesc
1High
2Medium
3Low
在模式文件中使用:
<Dimension name="Severity">
<Hierarchy hasAll="true" primaryKey="severity_id">
<Table name="severity"/>
<Level name="Severity" column="id" nameColumn="desc" uniqueMembers="true"/>
Hierarchy>
Dimension>
如果要为某行指定NULL值,忽略掉column的
元素,该列的值将默认为NULL。
3.3.8 成员属性和格式
在后面,我们会提到,一个level层级的定义中,包含了成员属性和成员格式的定义。
3.3.9 近似层级基数
层级元素允许指定可选属性:approxRowCount,指定这个属性能通过减少对层级,层次和维度基数的需求,达到改善效果的目标。这在通过XMLA连接到Mondrian时有重要影响。
3.3.10 缺省的度量属性
和 元素允许指定可选属性:defaultMeasure。 在
元素中指定defaultMeasure,用户能显式指定任意基本度量作为默认度量; 在
元素中指定,用户能显式指定任意VirtualCube度量作为默认度量。
需要注意的是:如果没有指定默认度量,那么将用cube数据立方中的第一个度量作为默认度量。而在virtual cube虚拟数据立方中,将用虚拟数据立方中定义的第一个cube立方中的第一个base measure基本度量作为其默认度量。
显式指定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 功能性依赖优化
在某些情况下,利用即将被处理的数据间的功能性依赖,可以进行效率优化。这些依赖通常是由业务规则产生的,且与生成这些数据的系统联系在一起,并通常不能通过数据本身进行推测。
功能性依赖在Mondrian中是通过
元素的dependsOnLevelValue属性和 元素的uniqueKeyLevelName属性来声明。
元素的dependsOnLevelValue属性用于表明成员属性的值是功能性依赖于 的值,而成员属性就定义在这个 中。也就是说,对于一个level层级的给定值,这个属性值是不变的。
元素的uniqueKeyLevelName属性用于表明在hierarchy层次中跟所有更高层的层级一起获得的给定level层级,相当于一个唯一的替换key值,以确保对于任意唯一的这些level层级值的联系,只有。。。
为了说明,可以想象一个层次,构造了美国的汽车制造和汽车牌照:
<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>
在上述例子中,我们知道一个给定的制造企业只存在于唯一的一个城市和州,而一辆汽车有一个颜色模式和一个修理维度,且牌照编号与唯一的州相互联系。因此,我们可以表明,所有这些成员属性都是功能性依赖于这些层级的值。
3.3.12 表格的Hint 提示
Mondrian支持对
元素的有限的数据库特征的hint提示,这些提示会传递导包含这些表格的SQL语句中。这类hints包括:
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.
比如:
<Table name="automotive_dim">
<Hint type="force_index">my_indexHint>
Table>
与功能性依赖优化一起,支持表格hints提示是在一个传统阶段,在Mondrian4.0中会有所变化。任何使用了表格hints提示的schema模式文件可能会需要修改,以匹配新的模式文件的语法规则。
你可能感兴趣的:(翻译,OLAP,Mondrian)