数据模式(Schema)定义

Mondrian Schema定义了一个多维数据库的逻辑和物理模型,包括立方体、维度、层次和度量等概念。它使用XML文件描述模式,并提供了创建和修改这些文件的工具。Schema文件可定义共享维度、角色以及访问权限,本文档主要关注其基本结构,如Cube、Dimension、Hierarchy和Measure的定义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据模式(Schema)定义

Created 星期一 24 十月 2016

模式(Schema)定义了一个多维数据库,它包一个含逻辑模型,并定义了逻辑模型到物理模型的映射
物理模型包含多维数据的存储方式:事实表、维表及其结构等。
逻辑模型包含了用于MDX 查询的结构,如数据立方体、维度、层次、成员、度量等。
Mondrian 使用一个XML 文件来描述模式。
Mondrian 包含一个创建、修改模式文件的Java桌面应用schema-workbench

模式文件

Schema 文件可以定义虚拟立方体,基于一个或多个基础立方体来建立。也可以定义角色以及角色的访问权限(对立方体、维度等)。有关虚拟立方体和角色的内容本文档不涉及。
Schema 文件主要结构如下(去掉了VirtualCube 和Role 等):
Schema 包含立方体、虚拟立方体、共享维度、角色

Cube 维度和度量的集合,以事实表为中心

Table 事实表

AggName 声明一个聚集表

aggElements 聚集表设置,结构见下

AggPattern 声明一批聚集表

aggElements

Dimension 维度

Hierarchy 维度层次

relation 物理表、视图,结构见下
Closure/ 映射父子层次关系
Level 一个层次

DimensionUsage 引用共享维度
Measure 度量

CalculatedMemberProperty/

CalculatedMember 计算成员
NamedSet 命名集合

Formula/ 公式

UserDefinedFunction/ 声明用户自定义函数

Relation 可以是Table、View、Inline Table、Join:
aggElement 可以是以下元素:

  • AggExclude
  • AggFactCount
  • AggIgnoreColumn
  • AggForeignKey
  • AggMeasure
  • AggLevel

示例:
personDemo.xml
<?xml version="1.0" encoding="UTF-8"?>
<Schema name="Mondrian"> <!--模型定义-->
<Cube name="Person"> <!--立方体 ,一个立方体有多个维度-->

<Table name="PERSON" /> <!--立方体对应的事实表 -->
<Dimension name="部门" foreignKey="USERID" > <!--定义维度 -->

<Hierarchy hasAll="true" primaryKey="USERID" allMemberName="所有部门" > <!--定义维度下面的层次,层次包含很多层 -->
<Table name="PERSON" alias="a"/> <!--定义维度获取数据的来源-维表 -->
<Level name="部门" column="DEPARTMENT" uniqueMembers="true" /> <!--定义层次的层,每个层对应数据库中对应的字段 -->
<Level name="姓名" column="USERNAME" uniqueMembers="true" />
</Hierarchy>

</Dimension>
<Dimension name="性别" foreignKey="USERID" >

<Hierarchy hasAll="true" primaryKey="USERID" allMemberName="所有性别">

<Table name="PERSON" alias="b" />

<Level name="性别" column="SEX" uniqueMembers="true" />
</Hierarchy>

</Dimension>
<Dimension name="专业技术资格类别" foreignKey="USERID" >

<Hierarchy hasAll="true" primaryKey="USERID" allMemberName="所有专业技术资格类别">

<Table name="PERSON" alias="c" />

<Level name="资格类别" column="ZYJSLB" uniqueMembers="true" />
</Hierarchy>

</Dimension>
<Dimension name="专业技术资格等级" foreignKey="USERID" >

<Hierarchy hasAll="true" primaryKey="USERID" allMemberName="所有专业技术资格等级">

<Table name="PERSON" alias="d" />

<Level name="资格等级" column="ZYJSDJ" uniqueMembers="true" />
</Hierarchy>

</Dimension>
<Dimension name="职系" foreignKey="USERID" >

<Hierarchy hasAll="true" primaryKey="USERID" allMemberName="所有职系">

<Table name="PERSON" alias="e" />

<Level name="职系" column="ZHIXI" uniqueMembers="true" />
</Hierarchy>

</Dimension>
<Dimension name="民族" foreignKey="USERID" >

<Hierarchy hasAll="true" primaryKey="USERID" allMemberName="所有民族">

<Table name="PERSON" alias="f" />

<Level name="民族" column="NATIONALITY" uniqueMembers="true" />
</Hierarchy>

</Dimension>
<Dimension name="学历" foreignKey="USERID" >

<Hierarchy hasAll="true" primaryKey="USERID" allMemberName="所有学历">

<Table name="PERSON" alias="g" />

<Level name="学历" column="XUELI" uniqueMembers="true" />
</Hierarchy>

</Dimension>
<Measure name="人数" column="USERID" aggregator="distinct count" /> <!--指标/度量,采用distinct count聚合 -->
</Cube>

</Schema>

度量

每个度量都有一个名字,一个事实表的列,一个聚集器(aggregator)。聚集起通常为sum,但count,min,max,avg,distinct-count 都是可以的;

  • 可选的datatype 属性可以是String、Integer、Numeric、Boolean、Date、Time 以及Timestamp,默认是Numeric,除了是count 或distinct-count,,这两个默认是Integer。
  • 可选的formatString 属性指定怎样打印这个值。
  • 度量可以有一个caption(标题)属性以便Member.getCaption()方法返回它而不是返回名字。

度量除了可以来自一个列,也可以使用一条SQL 表达式来计算。下面Promotion Sales 就是
一个例子:
<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>
为了一致,度量被视为一个特别的维度的成员,这个维度叫做Measures

缺省度量

Cube(和VirtualCube)元素允许指定可选的属性defaultMeasure
<Cube name="Sales" defaultMeasure="Unit Sales">
...

维度,层次

 

表映射

Dimension 元素有一个foreignKey 属性,它是事实表的列名
而Hierarchy 元素有一个primaryKey 属性
如果一个层次有不止一个表,可以使用primaryKeyTable 属性来区分。
Level 的column 定义了它的键,必须是这个层次所在表的列名。如果键是一个表达式,可以使用KeyExpression 元素子元素。
Level 元素的uniqueMembers 属性用于优化SQL 生成。

成员"ALL"
<Hierarchy hasAll="true" ...allMemberName="..."

多层次体系
一个维度可以由多个层次组成:
<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_id"以外,这些维度层次没有多少共同的地方,它们甚至没有使用同一个表。
把两个层次放到一个维度中的主要原因是,这对终端用户来说有更多意义。终端用户知道如果把"Time"层次放到一个轴上而把"Time Weekly"放到另一个轴上是没有意义的。如果两个层次是同一个维度,MDX 不会允许它们用于同一个查询中。

退化维度

退化维度是这样的一种维度:由于它过于简单而不值得为它创建一个维表。考虑下面的事实表:
产品 时间 支付方式 客户 数量 金额
55 20040106 Credit 123 3 3.54
78 20040106 Cash 89 1 20.00
199 20040107 ATM 3 2 2.99
55 20040106 Cash 122 1 1.18
假设我们为支付方式列的值创建一个维表:
payment_method
Credit
Cash
ATM
这个维表没什么意义,它只有三个取值,没有额外的信息,并产生了额外的连接开销。你可以创建一个退化维度,只要声明一个维度却不指定表,Mondrian 会认为这些列是来自事实表。
<Cube name="Checkout">
<Table name="checkout">
<Dimension name="Payment method">
<Hierarchy hasAll="true">
<!-- 这里没有Table 元素 -->
<Level name="Payment method" column="payment_method"
uniqueMembers="true"/>
</Hierarchy>
</Dimension>
<!-- 其他维度和度量-->
</Cube>
注意由于没有连接,Dimension 的foreignKey 属性是不必要的,并且Hierarchy 元素没有Table 子元素或primaryKey 属性。

 

内嵌表

http://mondrian.pentaho.com/documentation/schema.php#Inline_tables
InlineTable 构件允许你在模式文件定义一个数据集。你必须要声明列名,列类型(String 或Numeber),以及一个行集。像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>
这跟你的数据库有一个名为severity 的表以及下面的声明是一样的效果。
Severity 表:
id desc
1 High
2 Medium
3 Low

成员属性

成员属性通过Level 内的Property 元素来定义,例如:
<Level name="MyLevel" column="LevelColumn" uniqueMembers="true">

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

formatter="com.example.MyPropertyFormatter"/>
<Level/>
Property 的属性formatter 定义了一个属性格式化器,它修改了成员的
getPropertyFormattedValue()的默认行为。格式化器要实现mondrian.spi.PropertyFormatter接口。
一旦属性在模式中定义了以后,就可以在MDX 语句中使用它,通过member.Properties("属性名")函数。例如:
SELECT
{[Store Sales]} ON COLUMNS,
TopCount(Filter([Store].[Store Name].Members,
[Store].CurrentMember.Properties("Store Type") = "Supermarket"),
10, [Store Sales]) ON ROWS
FROM [Sales]
先筛选类型为"Supermarket"的商场(Filter 函数),再选出销售额最大的十家(TopCount 函数),把它们放在行上。

计算成员

假设你想创建一个度量,它的值不是来自事实表的列,而是来自一个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>
你可以给计算成员指定SOLVE_ORDER 属性,用于确定计算时的优先级:
<CalculatedMemberProperty name="SOLVE_ORDER" value="2000"/>

命名集合

<Cube name="Warehouse">
...
<NamedSet name="Top Sellers">
<Formula>
TopCount([Warehouse].[Warehouse Name].MEMBERS, 5,
[Measures].[Warehouse Sales])
</Formula>
</NamedSet>
</Cube>
在MDX 中使用:
SELECT
{[Measures].[Warehouse Sales]} ON COLUMNS,
{[Top Sellers]} ON ROWS
FROM [Warehouse]
WHERE [Time].[Year].[1997]

星型和雪花型

前面的立方体都是基于一个事实表、事实表上的维度以及连接到事实表的维表构建。这是最普通的映射方式,称为星型模式(Star Schema)。
一个维度可以基于不止一个维表,只要这些表能有良好定义的路径连接到事实表。这样定义维度的方式就是一个雪花型模式(Snowflake 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>
</Hierarchy>
</Dimension>
</Cube>
上面定义了一个由三个表组成的Product 维度。事实表连接到product(通过外键
product_id),而product 连接到product_class(通过外键product_class_id),而
product_class 连接到product_type(通过外键product_type_id)。我们需要一个Join 元素内嵌套一个Join 元素,因为Join 接收两个操作数。操作数可以是表、连接(Join)甚至查询。
注意这里Join 元素有一个rightAlias 属性。这是必要的,因为Join 的右边组件(嵌套的Join元素)由不止一个表组成。这种情形下leftAlias 属性是不必要的,因为leftKey 来自product表,没有歧义。

 

共享维度

当一个维度(维表)被多个立方体使用时,可以把它定义为共享维度(shared dimensions),在立方体内引用这个维度即可。因为一个共享维度不属于一个立方体,你必须给它一个明确的表(或其他数据源)。当你在某个立方体中使用它时,指定维度的外键。
<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>

闭包表

http://mondrian.pentaho.com/documentation/schema.php#Closure_tables
举例来说,闭包表(closure table)就是包含所有雇员/主管关系记录(不管深度如何)的SQL 表。

聚集表

http://mondrian.pentaho.com/documentation/schema.php#Aggregate_tables
设想一个CEO要运行一个销售报表,这个报表只包含一个数值:今年所有产品在所有地区的销售总额。为了获得这个数值,Mondrian产生像这样的一条SQL语句:
SELECT sum(store_sales)
FROM sales_fact, time
WHERE sales_fact.time_id = time.time_id AND time.year = 2005
并发送给数据库,而数据库要花好几分钟去执行它。这也容易理解,因为数据库要读取事实表中所有今年的记录(比方说,几百万条销售数据)然后汇总成一个汇总数。很明显,在这里,以及其他类似的时候,所需要的只是一个预先计算的概括性数据:聚集表。
聚集表跟基础事实表同时共存,包含着从事实表建立的预先聚集的度量。它在Mondrian的模式文件中注册,因此在一个特定的查询中能应用得上的时候,Mondrian 就能够选择某个聚集表而不是事实表。

Mondrian 有一个工具AggGen (aggregate generator)用于辅助设计和维护聚集表。它可以生成创建聚集表的SQL 语句,它也可以根据一条MDX 语句,给出能够优化这个查询的创建/插入聚集表的SQL 语句。

 

<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>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值