【问题标题】:Are Surrogate Primary Keys needed on a Fact table in a Data Warehouse?数据仓库中的事实表是否需要代理主键?
【发布时间】:2009-05-30 18:10:51
【问题描述】:

当我问我们的数据库设计人员为什么我们的 Fact 表没有 PK 时,我被告知表中没有一组列可以唯一标识一条记录,即使选择了所有列。当我建议我们在这种情况下创建一个标识列时,我被告知“我只是在浪费空间而且不需要它。”

我的感觉是,源系统中的每个表都应该有一个PK,即使它是一个标识列。鉴于数据仓库 (DW) 是来自其他系统的数据的接收者,如果无法绑定各个记录,我将如何确保 DW 中的数据准确反映源系统中的内容?如果您有一个失控的加载程序,该程序会破坏数据并运行了一周,您将如何与没有某种独特约束的实时事务源系统进行比较来调和这些差异?

【问题讨论】:

    标签: database-design business-intelligence


    【解决方案1】:

    数据仓库不一定是关系数据存储,尽管您可以选择将其设为一个,因此关系定义不一定适用。

    仅当您想对需要唯一标识符的数据执行某些操作时才需要主键(例如将其追溯到源,但这并不总是必需或必要甚至可能的);并且数据仓库中的数据通常可以以不需要主键的方式使用。具体来说,您可能不需要将行彼此区分开来。最常用于构造聚合值。

    时间不是构建数据仓库表的必要维度。

    这可能会带来心理上的不适,浪费空间是一个小问题,但你的同事是对的——PK不是必需的。

    【讨论】:

    • 时间不是必需的,但我没见过很多不关心历史的。
    • 即使是这样,时间戳的分辨率也会变化 - 通常只有一年的精确度 - 许多合法的相同数据点具有相同的值。
    • 我同意 le dorfier 的观点。因此,时间并不意味着交易必然是独一无二的。
    【解决方案2】:

    身份类型列是替换您的“候选”键之一的“代理”键(简单地说)。如果您无法识别没有它的行,则添加代理键列不会增加任何内容。这需要一个候选键。

    【讨论】:

    • 我想我同意。这对我来说表明源事务系统应该传递逻辑键(最终用户认为唯一标识记录的键)。你还能如何协调仓库和源事务系统之间的差异?
    • 将源系统行跟踪到事实表肯定与在事实表本身上提供唯一键不同。
    【解决方案3】:

    您至少应该在事实表上有一个自然键,以便您可以识别行并将它们与源相协调或在必要时跟踪更改。

    在 SQL Server 上,标识列免费为您提供代理键,而在使用序列的其他系统(例如 Oracle)上,可以相当容易地添加它。由于各种不同的原因,代理事实表键可能很有用。一些可能的应用是:

    • 一些工具喜欢在事实表上使用数字键,最好是单调递增的。这方面的一个例子是 MS SQL Server 分析服务,它非常喜欢为用于填充度量组的事实表提供一个数字的、单调递增的键。这对于增量负载尤其需要。

    • 如果您在事实表之间有任何关系(例如,熟悉保险的人的书面收入明细),那么合成键在这里很有帮助。

    • 如果您的维度与事实表存在 M:M 关系(例如 ICD 代码),那么事实表上的数字键可以简化此操作。

    • 如果您对事务有任何自联接要求(例如,某些事务是对其他事务的更正),那么合成密钥将简化处理这些事务。

    • 如果您在数据仓库中执行反向重述操作(即通过生成反转和重新陈述行来处理对事务数据的更改),那么您可以为同一个自然键拥有多个事实表行。

    否则,如果您没有任何东西以 1:M 关系加入事实表,那么合成密钥可能不会用于任何事情。

    【讨论】:

    • “你至少应该在事实表上有一个自然键,这样你就可以识别行并将它们与源相协调,或者在必要时跟踪更改”......这个自然键当然并不总是在事实表中是唯一的。
    【解决方案4】:

    没有主键的数据库表似乎是一个糟糕的设计选择,并且为不同类型的异常留出了很多空间,即如何删除或更新此类表中的单个记录?

    【讨论】:

    • 事实表不需要主键
    • 对不起,我的意思是有一个“ID”列。
    • 在 Oracle 中,您将使用 ROWID,因此该参数的相关性因 RDBMS 而异
    【解决方案5】:

    我会同意你的。

    “有人告诉我,表中没有一组列可以唯一标识一条记录,即使选择了所有列。” - 据我了解,这似乎破坏了关系数据库的一些基本内容。

    事实由附加值和维度的外键组成。时间是我所知道的每个维度模型所共有的一个明显维度。如果不出意外,包含时间戳的复合键肯定足够唯一。

    我想知道您的 DBA 是否对维度建模有很多了解。这是一种不同于正常关系型、交易型的思维方式。

    【讨论】:

    • 事实表是维度数据库,不是关系数据库
    • 准确地说,维度架构是一种特殊的关系样式,它使用星形或雪花排列的维度和事实表的组合来表示数据。
    【解决方案6】:

    你是对的——在某种程度上。没有主键,表不符合关系的最低定义。作为关系的基础,它不能允许重复的行。数据仓库设计中的表应该是关系型的,即使它们不是严格的标准形式。

    因此,行中必须有一些列(或一组列)用于唯一标识行。但它不一定是代理键的标识列。

    如果 Fact Table 没有一组列可以充当候选键的角色,那么这个 DW 中需要更多的 Dimension Tables,而 Fact Table 中需要更多的列。

    这个新的Dimension本身可能不是主键;它可以与事实表中的现有列组合以创建候选键。

    【讨论】:

    • 这意味着当要向事实表添加新行时,应进行搜索以查看是否已经存在具有相同唯一维度值集的行,这可能是除非基于键的单个散列值也被计算、存储和用于查找,否则这将是非常昂贵的业务。我从来没有真正想要这样做,除非绝对需要更新行,这通常不是这种情况,因为事实表中的指标往往是相加的,并且一个新行(具有重复的键值现有行)可以安全使用。
    • @DavidAldridge,每次对 RDBMS 中非 DW 表的插入都会检查表的主键,以确保不会插入重复的行。主键通常实现为索引,因此检查是 O(log n)。你是说DW表不这样做吗?还是说它的成本更高?
    • 这比不检查是否存在重复的成本更高。在 DW 事实表上通常也不需要它,因为 ETL 过程可以检查是否以前加载了整批记录(例如“2012-01-03 区域 3 的销售额”)。因此,您不必检查每条记录。此外,如果针对事实表的每个查询都是聚合,那么具有相同维度值集的记录也不是问题。
    • @DavidAldridge,很公平。看看性能的真正差异是什么会很有趣。为 DW 使用专门的存储引擎是一个很好的理由,因为为 OLTP 设计的存储引擎可能会将存储假定为按索引组织的表,这无论如何都会产生 PK 的成本。
    【解决方案7】:

    如果事实表位于星型模式的中心,则实际上存在候选键。如果将事实表中的所有外键放在一起,即指向维度表中行的外键,那就是候选键。

    将其声明为主键可能没有多大用处。它唯一能做的就是保护您免受流氓 ETL 过程的侵害。运营仓库的人可能会很好地处理 ETL。

    就索引和查询速度而言,星型模式与面向 OLTP 的数据库完全不同。经营仓库的人可能也有这个。

    在设计用于 OLTP 的数据库时,让表没有主键是不明智的。相同的考虑不会延续到仓库中。

    【讨论】:

    • “它唯一能做的就是保护您免受流氓 ETL 过程的侵害”仅此考虑不足以保证进行 PK 吗?
    • 代理自动生成的 PK 不能防止恶意 ETL 进程。一些仓库 DBA 更信任 ETL 程序,而不是随机应用程序。这种信任可能是错误的,所以你的观点值得考虑。
    • 这可能是真的,但维度键可能不会形成候选键。
    【解决方案8】:

    我一直认为一个表应该按照最常见的查询或性能冲击来排序,因此表的聚集索引应该与最困难或最常见的查询一致。

    主键不必是聚集索引,所以我知道您可能想知道我的目标是什么,但我更关心的是聚集索引而不是主键(老实说,它们通常遵循每个其他)。

    所以对我来说最初的问题不是“我应该在事实表上有一个代理主键吗?”但更像是“我应该在事实表上有一个聚集索引吗?”我认为答案是肯定的,你应该有一个(是的,这个网站上还有其他帖子涵盖了这个问题,但我仍然认为在这里值得一提,以防这是人们真正提出的问题,尽管措辞错误)

    有时您需要代理键,但我衷心建议代理键不是表的聚集索引。这样做会按照无意义的代理键对表进行排序。 (通常人们在表中添加一个代理标识列,并使其成为主键,默认情况下也是聚集索引)

    那么在哪些列上创建聚集索引?我个人喜欢事实表的日期,为此您可能会添加一些其他维度的 FK 以获得唯一性,但这会增加大小并且可能不会提供任何好处,因为为了使索引有用,必须引用相关维度(在生成密钥的重要性顺序)。

    为了解决这个问题(以及我在这里回答这个问题的原因),我认为您应该添加一个代理项,然后在日期键上创建聚集索引,然后是代理项(按此顺序)。我这样做是因为单独的日期不会构成唯一的行,而是添加代理将。这样可以保持数据按日期排序,这有助于所有其他非聚集索引,同时保持聚集索引大小合理。

    此外,随着数据的增长,您可能希望对其进行分区,在这种情况下,您将需要一个分区键,该分区键始终是日期。以日期作为键的主要部分构建聚集索引使这更容易。通过分区,您现在可以使用滑动窗口技术来归档旧数据或加载。

    【讨论】:

      【解决方案9】:

      每行没有唯一标识符比最初看起来更糟糕。当然,这很不稳定,而且很容易无意中删除一些行。

      但性能也差很多。每次您最终要求数据库为您获取带有EmployeeType = 'Manager' 的员工行时,您都在进行字符串比较。标识符更快更好。

      此外,存储很便宜,在这种情况下,我想对空间的影响将小于四分之一个百分点如果 - 作为一个数据仓库,您可能正在为数 TB 的数据设计数据。

      【讨论】:

        【解决方案10】:

        http://www.ralphkimball.com/html/controversies.html

        寓言:

        事实表的主键由所有引用的维度外键组成。

        事实:

        事实表通常有 10 个或更多外键连接到维度表的主键。然而,行唯一性通常只需要事实表的外键引用的一个子集。大多数事实表都有一个主键,它由外键的连接/复合子集组成。

        【讨论】:

        【解决方案11】:

        使用维度代理键的组合作为事实表的主键并不适用于所有情况。考虑存在三个维度a、b和c的情况。在大多数设计中,我们通常有一个“未知”的维度行,假设我总是为该行分配 -1 的代理键。我可以很容易地在我的事实表中有两行具有键 a=n1、b=n2 和 c=-1,即重复键,因为这两行没有得到维度 c 的有效值,因此都解析为未知行。

        【讨论】:

        • 但是在这种情况下,你怎么知道这些行实际上是有效数据还是不是?如果所有属性都是未知的,那么数据有什么用?
        • 您可能会遇到所有值都已知但仍然存在重复的情况,这仅仅是因为插入新记录比搜索具有相同集合的记录的存在更有效n 维值并更新它。
        【解决方案12】:

        您在这里混淆了两个问题——识别事实表中的唯一记录,以及从源系统到事实表的跟踪记录。

        在后一种情况下,源系统中的单个记录很可能有多个事实表记录。想象一个源系统记录,它表示资金从一个帐户转移到另一个帐户。可能有两条事实表记录来表示这一点,一条用于借方账户,一条用于贷方账户。此外,可能有多个事实记录来表示源系统记录在其生命周期的不同点的不同状态。

        对于事实表的主键问题,真的没有一个“正确”的答案。您可能想要一些理想/基本特征(例如,单个记录的身份可以在系统用户之间轻松通信,或者单个记录可以轻松删除或更新)。但是,对于 Oracle 系统,ROWID 可能会很好地做到这一点,只要它是否偶尔更改无关紧要。

        实际上,维护单个合成密钥的开销非常小,无论如何您都可以这样做。您可能会选择不为其编制索引,因为索引将比列本身消耗更多的资源。

        【讨论】:

          猜你喜欢
          • 2017-09-27
          • 1970-01-01
          • 2018-06-05
          • 2016-05-07
          • 1970-01-01
          • 2013-05-13
          • 2013-06-07
          • 1970-01-01
          相关资源
          最近更新 更多