如何避免星型模式中事实表之间的连接?

Posted

技术标签:

【中文标题】如何避免星型模式中事实表之间的连接?【英文标题】:How to avoid joins between fact tables in a star schema? 【发布时间】:2017-03-06 15:38:07 【问题描述】:

我正在尝试使用星型模式为我的数据仓库建模,但在避免事实表之间的连接时遇到了问题。 为了简单地了解我的问题,我想收集在我的操作系统上发生的所有事件。所以,我可以创建一个事实表event,其中包含一些维度,例如datetimeuser。问题是我想收集不同类型的事件:硬件事件和软件事件。 问题是这些事件的维度不同。例如,对于硬件事件,我可以有 physical_componentrelated_driver 尺寸,对于软件事件,software_nameonline_application 尺寸(这只是一些示例,要记住的想法是事实event 可以专门化为一些特定维度的特定事件。 在关系模型中,我将有 3 个这样组织的表: 问题是:如何在星型模式中处理事实表之间的连接?


我想象了 4 个想法,但我不确定其中一个是否适合这种情况。第一个是将模型保留在关系数据库中并添加如下维度表: 在这种情况下,问题是事实表之间仍然存在连接,需要在所有查询中使用JOIN SQL 语句。
第二个是只创建 2 个将复制共享维度(日期时间和用户)的事实表,并创建一个汇总所有事件的物化视图事件: 这里的问题是:如果我想对物化视图进行查询会发生什么?根据我在 Oracle 文档中阅读的内容,我们不必直接在物化视图上进行查询,但我们必须让查询重写过程使其工作。
第三个是只创建一个事实表,该表将包含事件(硬件或软件)的所有可能信息: 这一次,问题是我的事实表将包含很多NULL 值。
最后一个是创建 3 个事实表(这次没有物化视图),如下所示: 这一次,问题是所有事件都存在于事实表event 和它自己的表中。因为我们将存储大量数据,所以我不确定这种复制是否是个好主意。 那么最好的解决方案是什么?还是存在第五种解决方案?

【问题讨论】:

软件和硬件事件在它们的数据结构中有多相似(内容方面,而不是列名)?如果类似,您可以只使用像“源”这样的单个维度,其中一些共享列和一些非共享列设置为“n/a”,用于它们不适用的类型。 @Cyrus 软件和硬件事件的结构(列数)和内容(每个表的列之间没有链接)完全不同。所有共享列都在事件表中。 【参考方案1】:

从您的描述和您随后的 cmets 到其他答案,我会说选项 2 或选项 4 是从维度建模角度建模事物的正确方法。每个事实都应该是对业务流程的衡量,并且软件和硬件事件的维度似乎有很大不同,因此它们需要单独存储。

然后,还有一种情况是将单独的事件表存储为视图、物化视图或普通表,以存储常见的事物。

一旦您确定这是对事物进行“逻辑”建模的正确方法,您就需要平衡性能、可维护性、可用性和存储。 对于维度建模,查询的可用性和性能是重中之重(否则您可能根本不使用维度模型),ETL 中的额外工作和所需的额外空间是值得付出的代价。

非物化视图会以性能为代价为您节省空间,但您可能可以给它提供一个或两个足够出色的索引来缓解这种情况。物化视图将以存储为代价提供性能。

我很想创建两个带有索引的事实表和一个非物化视图,并在采取进一步的性能增强步骤之前看看它的性能如何。 1000 万行事实还不错,它可能仍然可以执行。

可以直接查询物化视图。但是,如果您愿意,可以使用 Oracle 的查询重写功能,以便在查询原始表时将物化视图用作性能增强器,如索引。 详情见这里:http://www.sqlsnippets.com/en/topic-12918.html 您是选择在查询重写模式下使用它还是仅将其用作视图,取决于您是希望用户了解这个额外的表,还是希望它只是作为一个乐于助人的朋友坐在后台。

【讨论】:

根据您的最后一句话,您更喜欢选择选项2。但是如果非物化视图给我带来不好的性能怎么办(高性能是我问题的关键)?我想我应该创建一个物化视图。但是因为不可能直接查询物化视图,我如何确定我的查询将被重写以使用这个物化视图?或者,如果性能不好,我应该使用第四个模型? 是的,如果非物化视图给您带来不好的性能,我同意,可以创建物化视图。可以直接查询物化视图。但是,如果您选择,您可以设置物化视图,以便可以使用物化视图中的信息来满足对原始表的查询,而不会更改查询。这样,您的查询将保持不变,并且将自动使用具体化视图来帮助加快查询速度。有关更多描述,请参见:sqlsnippets.com/en/topic-12918.html。我将编辑答案。【参考方案2】:

在您的场景中似乎没有理由将这两种类型的事件结合或联系起来。话虽如此,您可能有一些您没有描述的原因(例如,从多个系统收集日志并希望轻松地将它们一起查看)。

所以我的建议是制作一个包含硬件和软件维度键的单一事实表。其中之一将始终为 0 或 -1(= 默认“n/a”记录)。

这使您无需 UNION 语句或其他复杂逻辑即可将它们聚合在一起,甚至可以支持与硬件和软件相关的事件(如果它们将来出现)。

【讨论】:

这是我作为第三选择提出的想法。但是有很多 NULL 值有问题吗?您打算用 0 或 -1 替换它们,但是根据这个答案:***.com/a/8310891/7334162,这对于未来的计算似乎不是一个好主意,对吧? @Pierre 度量值在源数据中不存在时应为 NULL,但维度键永远不应存在,默认记录更有意义。 当我使用这种模型只对一种事件(例如硬件)提出请求而不是每种类型都有一个事实表时,你不认为它会花费更多时间吗?事件?还是您认为创建物化视图来区分硬件或软件事件更好? 数据库已针对处理此类查询进行了优化,因此除非您拥有数百万/数十亿行,否则这无关紧要。不过,所有这些连接都需要正确的主键和索引定义。 我计划存储超过1000万条记录。我正在寻找最优化的模型,以最大限度地减少我将提出的每个请求。【参考方案3】:

您永远不会/很少将事实表连接在一起。您可以加入共享(一致)维度(即每小时软件事件数与每小时硬件事件数相比)的聚合事实。

对我来说,在查看维度建模时,您总是必须考虑将要问的各种问题。

【讨论】:

就我而言,我将查询数据库以获取有关所有事件(硬件和软件)的信息,这就是我需要一个全局事件事实表的原因;但我也会查询数据库以获取有关特定事件(硬件或软件)的信息,这就是为什么我更喜欢使用单独的事实表。之后,我想创建物化视图来获取聚合信息。这就是为什么我仍然不知道哪种型号在我的情况下更好。 我会用两个事实表建模。还要记住,维度是根据数据行为更务实地放入表格中的,所以不要忘记垃圾维度和退化维度的概念。 退化维度与此有什么关系? 在决定有两个单独的事实表时,当涉及到此处未显示的整个模型时,退化维度可能会有更多明显的差异。对我来说,建模问题的症结隐藏在“获取有关所有事件的信息”或“需要全局事件表”的含义背后。当然,任何模型都将拥有所有数据。问题是如何安排数据,以便执行通常预期的用例。聚合时,它们是如何使用的?向下钻取时,它们是如何使用的? 为了详细说明退化维度,软件事件的来源可能有很多堆栈跟踪和文本数据转储变量状态等。对于硬件,可能有更多硬件状态和配置数据的文本.我试图了解在事实表中将它们组合在一起的情况,因为它们将在同一分析中组合在一起,而不是用户或日期或业务单位/部门位置等一致维度。【参考方案4】:

事件应该是一个单一的事实。如果将它们一分为二,您将很难在两者之间进行聚合。

如有必要,您可以有单独的硬件和软件属性维度,但您应该有一个通用的事件维度,即使它只是一个带有一些简单属性的垃圾维度,例如类型(硬件/软件)、重要性(高、低)等

在旁注中,我通常会看到带有箭头的图表,这些箭头来自于尺寸的事实。事实表键查看维度而不是其他方式。

【讨论】:

我不确定创建一个垃圾维度是否足够,因为有很多仅用于硬件的特定维度和许多仅用于软件的其他特定维度(而不仅仅是离散变量)。关于图表,你是对的,我的错。

以上是关于如何避免星型模式中事实表之间的连接?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle——星型查询

SQLAlchemy 中的星型模式

数据仓库星型模式的维度表和事实表中的数据如何?

使用 Python 从 csv 文件创建星型模式

星型模式

插入星型模式