这个多维模型中有什么不正确的地方?
What could be incorrect in this multidimensional model?
我想在关系数据库 (ROLAP) 中使用星型模式构建一个简单的多维数据模型。为此,我创建了一个事实 table 和两个 table 维度。首先,我从操作源复制数据并处理这些数据(一些简化的 ETL 过程)。
在我的模型中只有两个维度:date
和 status
。 Measure:某些状态的数量(一段时间)。
时间维度table:
CREATE TABLE [dbo].[tbl_date_dim] (
[ID][int] IDENTITY(1,1) NOT NULL,
[date_key][int] NOT NULL primary key,
[Year][int] NOT NULL,
[Month][int] NOT NULL,
[Day][int] NOT NULL
);
有一个 table - tbl_application
- 其中存储了整个时间范围(字段 VersionDate
)。所以,时间维度table我是这样填的:
INSERT INTO [dbo].[tbl_date_dim]
([date_key],
[Year],
[Month],
[Day])
(
SELECT DISTINCT
CAST(YEAR(VersionDate) as VARCHAR(4)) +
RIGHT('00' + CAST(MONTH(VersionDate) as VARCHAR(2)) ,2) +
RIGHT('00' + CAST(DAY(VersionDate) as VARCHAR(2)), 2) as 'date_key',
YEAR(inner_data.VersionDate) as 'Year',
MONTH(inner_data.VersionDate) as 'Month',
DAY(inner_data.VersionDate) as 'Day'
FROM (
SELECT
VersionDate
FROM [dbo].[tbl_application]
) AS inner_data
);
状态维度table:我使用整个现有table tbl_applicationstatus
.
接下来,我创建一个事实 table。它包含维度 tables 和度量的外键。
CREATE TABLE [dbo].[tbl_olap_fact] (
[ID][int] IDENTITY(1,1) NOT NULL,
[status_id][int] NOT NULL, // FK
[date_dim][int] NOT NULL, // FK
[staus_name] varchar(100) NOT NULL, // Non additive measure
[transaction_id][int] NOT NULL, // Additive measure
CONSTRAINT [PK_tbl_olap_fact] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
transaction_id
- 这个字段,我将汇总(状态数)。
接下来,我添加事实table和维度table之间的关系:
ALTER TABLE [dbo].[tbl_olap_fact] ADD CONSTRAINT [FK_tbl_olap_fact_tbl_date_dim] FOREIGN KEY([date_dim])
REFERENCES [dbo].[tbl_date_dim] ([date_key]);
ALTER TABLE [dbo].[tbl_olap_fact] ADD CONSTRAINT [FK_tbl_olap_fact_tbl_applicationstatus] FOREIGN KEY([status_id])
REFERENCES [dbo].[tbl_applicationstatus] ([ID]);
那我填事实table:
INSERT INTO [dbo].[tbl_olap_fact]
([transaction_id],
[status_id],
[staus_name],
[date_dim])
(
SELECT DISTINCT
core.id as 'transaction_id',
core_status.ID as 'status_id',
core_status.name as 'status_name',
CAST(YEAR(core.VersionDate) as VARCHAR(4)) +
RIGHT('00' + CAST(MONTH(core.VersionDate) as VARCHAR(2)) ,2) +
RIGHT('00' + CAST(DAY(core.VersionDate) as VARCHAR(2)), 2) as 'date_dim'
FROM
[dbo].[tbl_application] as core
inner join tbl_applicationstatus as core_status
on core.ApplicationStatusID = core_status.ID
WHERE IsRaw = 0
);
作为 OLAP 服务器,我正在使用 Mondrian。定义多维数据库逻辑模型的Mondrian schema:
<Schema name="olap_schema">
<Dimension type="TimeDimension" visible="true" highCardinality="false" name="Date first dim">
<Hierarchy name="date_hierarchy" visible="true" hasAll="true" primaryKey="date_key" description="">
<Table name="tbl_date_dim" schema="dbo">
</Table>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Year"
nameColumn="Year"
type="Numeric"
uniqueMembers="true"
levelType="TimeYears"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Month"
nameColumn="Month"
ordinalColumn="Month"
type="Numeric"
uniqueMembers="false"
levelType="TimeMonths"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Day"
nameColumn="Day"
ordinalColumn="Day"
type="Numeric"
uniqueMembers="false"
levelType="TimeDays"
hideMemberIf="Never"
description="">
</Level>
</Hierarchy>
</Dimension>
<Dimension type="TimeDimension" visible="true" highCardinality="false" name="Date second dim">
<Hierarchy name="date_hierarchy" visible="true" hasAll="true" primaryKey="date_key" description="">
<Table name="tbl_date_dim" schema="dbo">
</Table>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Year"
nameColumn="Year"
type="Numeric"
uniqueMembers="true"
levelType="TimeYears"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Month"
nameColumn="Month"
ordinalColumn="Month"
type="Numeric"
uniqueMembers="false"
levelType="TimeMonths"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Day"
nameColumn="Day"
ordinalColumn="Day"
type="Numeric"
uniqueMembers="false"
levelType="TimeDays"
hideMemberIf="Never"
description="">
</Level>
</Hierarchy>
</Dimension>
<Dimension type="StandardDimension" visible="true" highCardinality="false" name="Status dimension">
<Hierarchy name="status_hierarchy" visible="true" hasAll="true" primaryKey="ID" description="">
<Table name="tbl_applicationstatus" schema="dbo">
</Table>
<Level name=""
visible="true"
table="tbl_applicationstatus"
column="Name"
nameColumn="Name"
type="String"
uniqueMembers="true"
levelType="Regular"
hideMemberIf="Never"
description="">
</Level>
</Hierarchy>
</Dimension>
<Cube name="enrollment_cube" caption="" visible="true" description="" cache="true" enabled="true">
<Table name="tbl_olap_fact" schema="dbo">
</Table>
<DimensionUsage source="Date first dim" name="X axis" caption="" visible="true" foreignKey="date_dim" highCardinality="false">
</DimensionUsage>
<DimensionUsage source="Date second dim" name="Y axis" caption="" visible="true" foreignKey="date_dim" highCardinality="false">
</DimensionUsage>
<DimensionUsage source="Status dimension" name="Z axis" caption="" visible="true" foreignKey="status_id" highCardinality="false">
</DimensionUsage>
<Measure name="TotalCount" column="transaction_id" aggregator="count" caption="Total" visible="true">
</Measure>
</Cube>
</Schema>
作为 OLAP 客户端,我正在使用 Saiku Analytics。
基本上,我得到了正确的数据 - 但不是很确定。例如,我用来填充事实 table 的方式是否正确?我是否正确构建了 ETL 过程?这是一个测试模式,我在构建数据仓库和多维模型方面做了一些实验。
在此免责声明:我从未使用过 Mondrian,我在这里给出的建议是通用的,紧贴 Kimball 的做事方式。如果 Mondrian 需要对模式进行一些特定的更改,那就去吧。
tbl_date_dim
这是一个好的开始 - 日期维度很重要。您可能会发现您还需要一个单独的时间维度(例如,如果您想按小时查看事物)。
我会为此删除 ID 列。它有什么作用?每个 YYYYMMDD 值都是唯一的,这里的标准模式是简单地将其用作 table.
的代理键
您可能会发现您还想向此 table 添加更多的列;现在,我建议添加一个包含实际日期的日期列,因为这是该维度的业务键。您应该始终在每个维度中同时拥有代理键和业务键。
如果您还不熟悉这个术语,请仔细阅读业务键的概念 - 它基本上是您的组织在引用特定维度成员时会使用的一个有意义的名称,通常是某种名称。人们经常犯错误,使用源系统中无意义的代码或无用的缩写,但这些确实应该避免。
我建议以不同的方式填充 tbl_date_dim。当然,您的操作方式暂时可行,但您的日期维度最终会被许多其他 table 引用,您可能会发现它缺少您需要的日期。标准的解决方案只是编写脚本,或者甚至将电子表格放在一起并导入,并在过去和未来包含一个 suitable 范围的日期。它从来没有那么大,所以大小不是真正的问题。如果您想编写脚本,您应该能够通过一些搜索找到可以为您完成工作的脚本。
tbl_applicationstatus
很难对此发表评论,因为您没有显示 DDL。不过,您很可能不应该使用整个源 table。确保您在数据仓库中创建了代理键(标识列可以,将其命名为 application_status_key 之类的名称)和业务键。这大概是status_name。
tbl_olap_fact
一个事实 table 应该是同一粒度的一个或多个度量,以及维度 table 的外键。理解 grain 很关键,你应该考虑 grain 是什么,然后给这个事实起一个有意义的名字来反映它。例如,如果您的事实将具有与交易相关的多个措施之一,那么 tbl_transaction_fact 可能是一个好名字。
有点不清楚您在此处尝试测量什么,因为您没有解释 tbl_application 来源 table 中的数据,但看起来您可能正在尝试计算在设置某个应用程序状态时执行的交易数量?请注意,您实际上在这里没有任何附加措施;附加度量是可以求和的东西 - 货币金额、多个项目等。
如果你只是以此为例,我强烈建议你首先考虑一个你希望你的多维数据集回答的问题,该问题涉及附加的东西(即类似于 "How much are all applications created in January worth?" 的东西)如果您的应用程序具有货币价值),然后建模可以回答该问题的东西。
您当然也可以进行计数,但可能没有必要为源导入 transaction_id 值 - 您可以只计算您的 ID 列。
您应该从事实 table 中删除 status_name,因为您应该通过链接到您的状态维度并使用那里的名称列来实现这一点。状态名称是该维度的业务键,而不是非附加度量。
当您填充事实时,通常的模式是使用业务键,然后从维度本身或查找 table 中选取代理键,然后加载仅代理键指向维度的事实。
有一个 really handy Kimball guide 简要概述了各种技术。它非常方便,既可以作为查找概念的初始位置,也可以在需要提醒时回顾 - 我建议通读并保存它。
我想在关系数据库 (ROLAP) 中使用星型模式构建一个简单的多维数据模型。为此,我创建了一个事实 table 和两个 table 维度。首先,我从操作源复制数据并处理这些数据(一些简化的 ETL 过程)。
在我的模型中只有两个维度:date
和 status
。 Measure:某些状态的数量(一段时间)。
时间维度table:
CREATE TABLE [dbo].[tbl_date_dim] (
[ID][int] IDENTITY(1,1) NOT NULL,
[date_key][int] NOT NULL primary key,
[Year][int] NOT NULL,
[Month][int] NOT NULL,
[Day][int] NOT NULL
);
有一个 table - tbl_application
- 其中存储了整个时间范围(字段 VersionDate
)。所以,时间维度table我是这样填的:
INSERT INTO [dbo].[tbl_date_dim]
([date_key],
[Year],
[Month],
[Day])
(
SELECT DISTINCT
CAST(YEAR(VersionDate) as VARCHAR(4)) +
RIGHT('00' + CAST(MONTH(VersionDate) as VARCHAR(2)) ,2) +
RIGHT('00' + CAST(DAY(VersionDate) as VARCHAR(2)), 2) as 'date_key',
YEAR(inner_data.VersionDate) as 'Year',
MONTH(inner_data.VersionDate) as 'Month',
DAY(inner_data.VersionDate) as 'Day'
FROM (
SELECT
VersionDate
FROM [dbo].[tbl_application]
) AS inner_data
);
状态维度table:我使用整个现有table tbl_applicationstatus
.
接下来,我创建一个事实 table。它包含维度 tables 和度量的外键。
CREATE TABLE [dbo].[tbl_olap_fact] (
[ID][int] IDENTITY(1,1) NOT NULL,
[status_id][int] NOT NULL, // FK
[date_dim][int] NOT NULL, // FK
[staus_name] varchar(100) NOT NULL, // Non additive measure
[transaction_id][int] NOT NULL, // Additive measure
CONSTRAINT [PK_tbl_olap_fact] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
transaction_id
- 这个字段,我将汇总(状态数)。
接下来,我添加事实table和维度table之间的关系:
ALTER TABLE [dbo].[tbl_olap_fact] ADD CONSTRAINT [FK_tbl_olap_fact_tbl_date_dim] FOREIGN KEY([date_dim])
REFERENCES [dbo].[tbl_date_dim] ([date_key]);
ALTER TABLE [dbo].[tbl_olap_fact] ADD CONSTRAINT [FK_tbl_olap_fact_tbl_applicationstatus] FOREIGN KEY([status_id])
REFERENCES [dbo].[tbl_applicationstatus] ([ID]);
那我填事实table:
INSERT INTO [dbo].[tbl_olap_fact]
([transaction_id],
[status_id],
[staus_name],
[date_dim])
(
SELECT DISTINCT
core.id as 'transaction_id',
core_status.ID as 'status_id',
core_status.name as 'status_name',
CAST(YEAR(core.VersionDate) as VARCHAR(4)) +
RIGHT('00' + CAST(MONTH(core.VersionDate) as VARCHAR(2)) ,2) +
RIGHT('00' + CAST(DAY(core.VersionDate) as VARCHAR(2)), 2) as 'date_dim'
FROM
[dbo].[tbl_application] as core
inner join tbl_applicationstatus as core_status
on core.ApplicationStatusID = core_status.ID
WHERE IsRaw = 0
);
作为 OLAP 服务器,我正在使用 Mondrian。定义多维数据库逻辑模型的Mondrian schema:
<Schema name="olap_schema">
<Dimension type="TimeDimension" visible="true" highCardinality="false" name="Date first dim">
<Hierarchy name="date_hierarchy" visible="true" hasAll="true" primaryKey="date_key" description="">
<Table name="tbl_date_dim" schema="dbo">
</Table>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Year"
nameColumn="Year"
type="Numeric"
uniqueMembers="true"
levelType="TimeYears"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Month"
nameColumn="Month"
ordinalColumn="Month"
type="Numeric"
uniqueMembers="false"
levelType="TimeMonths"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Day"
nameColumn="Day"
ordinalColumn="Day"
type="Numeric"
uniqueMembers="false"
levelType="TimeDays"
hideMemberIf="Never"
description="">
</Level>
</Hierarchy>
</Dimension>
<Dimension type="TimeDimension" visible="true" highCardinality="false" name="Date second dim">
<Hierarchy name="date_hierarchy" visible="true" hasAll="true" primaryKey="date_key" description="">
<Table name="tbl_date_dim" schema="dbo">
</Table>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Year"
nameColumn="Year"
type="Numeric"
uniqueMembers="true"
levelType="TimeYears"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Month"
nameColumn="Month"
ordinalColumn="Month"
type="Numeric"
uniqueMembers="false"
levelType="TimeMonths"
hideMemberIf="Never"
description="">
</Level>
<Level name=""
visible="true"
table="tbl_date_dim"
column="Day"
nameColumn="Day"
ordinalColumn="Day"
type="Numeric"
uniqueMembers="false"
levelType="TimeDays"
hideMemberIf="Never"
description="">
</Level>
</Hierarchy>
</Dimension>
<Dimension type="StandardDimension" visible="true" highCardinality="false" name="Status dimension">
<Hierarchy name="status_hierarchy" visible="true" hasAll="true" primaryKey="ID" description="">
<Table name="tbl_applicationstatus" schema="dbo">
</Table>
<Level name=""
visible="true"
table="tbl_applicationstatus"
column="Name"
nameColumn="Name"
type="String"
uniqueMembers="true"
levelType="Regular"
hideMemberIf="Never"
description="">
</Level>
</Hierarchy>
</Dimension>
<Cube name="enrollment_cube" caption="" visible="true" description="" cache="true" enabled="true">
<Table name="tbl_olap_fact" schema="dbo">
</Table>
<DimensionUsage source="Date first dim" name="X axis" caption="" visible="true" foreignKey="date_dim" highCardinality="false">
</DimensionUsage>
<DimensionUsage source="Date second dim" name="Y axis" caption="" visible="true" foreignKey="date_dim" highCardinality="false">
</DimensionUsage>
<DimensionUsage source="Status dimension" name="Z axis" caption="" visible="true" foreignKey="status_id" highCardinality="false">
</DimensionUsage>
<Measure name="TotalCount" column="transaction_id" aggregator="count" caption="Total" visible="true">
</Measure>
</Cube>
</Schema>
作为 OLAP 客户端,我正在使用 Saiku Analytics。
基本上,我得到了正确的数据 - 但不是很确定。例如,我用来填充事实 table 的方式是否正确?我是否正确构建了 ETL 过程?这是一个测试模式,我在构建数据仓库和多维模型方面做了一些实验。
在此免责声明:我从未使用过 Mondrian,我在这里给出的建议是通用的,紧贴 Kimball 的做事方式。如果 Mondrian 需要对模式进行一些特定的更改,那就去吧。
tbl_date_dim
这是一个好的开始 - 日期维度很重要。您可能会发现您还需要一个单独的时间维度(例如,如果您想按小时查看事物)。
我会为此删除 ID 列。它有什么作用?每个 YYYYMMDD 值都是唯一的,这里的标准模式是简单地将其用作 table.
的代理键您可能会发现您还想向此 table 添加更多的列;现在,我建议添加一个包含实际日期的日期列,因为这是该维度的业务键。您应该始终在每个维度中同时拥有代理键和业务键。
如果您还不熟悉这个术语,请仔细阅读业务键的概念 - 它基本上是您的组织在引用特定维度成员时会使用的一个有意义的名称,通常是某种名称。人们经常犯错误,使用源系统中无意义的代码或无用的缩写,但这些确实应该避免。
我建议以不同的方式填充 tbl_date_dim。当然,您的操作方式暂时可行,但您的日期维度最终会被许多其他 table 引用,您可能会发现它缺少您需要的日期。标准的解决方案只是编写脚本,或者甚至将电子表格放在一起并导入,并在过去和未来包含一个 suitable 范围的日期。它从来没有那么大,所以大小不是真正的问题。如果您想编写脚本,您应该能够通过一些搜索找到可以为您完成工作的脚本。
tbl_applicationstatus
很难对此发表评论,因为您没有显示 DDL。不过,您很可能不应该使用整个源 table。确保您在数据仓库中创建了代理键(标识列可以,将其命名为 application_status_key 之类的名称)和业务键。这大概是status_name。
tbl_olap_fact
一个事实 table 应该是同一粒度的一个或多个度量,以及维度 table 的外键。理解 grain 很关键,你应该考虑 grain 是什么,然后给这个事实起一个有意义的名字来反映它。例如,如果您的事实将具有与交易相关的多个措施之一,那么 tbl_transaction_fact 可能是一个好名字。
有点不清楚您在此处尝试测量什么,因为您没有解释 tbl_application 来源 table 中的数据,但看起来您可能正在尝试计算在设置某个应用程序状态时执行的交易数量?请注意,您实际上在这里没有任何附加措施;附加度量是可以求和的东西 - 货币金额、多个项目等。
如果你只是以此为例,我强烈建议你首先考虑一个你希望你的多维数据集回答的问题,该问题涉及附加的东西(即类似于 "How much are all applications created in January worth?" 的东西)如果您的应用程序具有货币价值),然后建模可以回答该问题的东西。
您当然也可以进行计数,但可能没有必要为源导入 transaction_id 值 - 您可以只计算您的 ID 列。
您应该从事实 table 中删除 status_name,因为您应该通过链接到您的状态维度并使用那里的名称列来实现这一点。状态名称是该维度的业务键,而不是非附加度量。
当您填充事实时,通常的模式是使用业务键,然后从维度本身或查找 table 中选取代理键,然后加载仅代理键指向维度的事实。
有一个 really handy Kimball guide 简要概述了各种技术。它非常方便,既可以作为查找概念的初始位置,也可以在需要提醒时回顾 - 我建议通读并保存它。