【问题标题】:Eliminating redundant relationships when modeling Header/Detail relationships?在对 Header/Detail 关系建模时消除冗余关系?
【发布时间】:2010-03-04 01:39:50
【问题描述】:

我有一个看起来像这样的模型:

一个账户有多个分行,每个报表都是为一个账户生成的。该模型是多余的,因为可以从事务上的 BranchID 推断出 Account(标题上的 AccountID)(语句将始终具有一个或多个事务)。

是否应该从 StatementHeader 中删除 AccountID,或者这种冗余级别可以吗?还是有更好的解决方案?

【问题讨论】:

  • 我会避免在每个表中使用“ID”作为主键的名称;您最终会在模型中加载大量它们,并且永远无法确定您指的是哪个。如果您使用代理键,那么我建议您在整个模型中使用相同的名称。您已经将 AccountID 作为外键,因此请使用相同的名称作为主键。
  • 不幸的是,命名约定是由其他人设置的,所以我无法真正更改它。我很欣赏你的理由,因为我有同样的担忧。
  • StatementHeader的实体是什么?具有相同 StatementHeaderID 的语句在现实世界中是否相互连接,或者它们共享的只是 AccountID 和 StatementDate?后者会使 StatementHeader 类似于日历,将 SatementHeaderID 伪装成日期......
  • Account和Branch是什么关系? Branch 是否应该有一个外键 AccountID,如图所示?
  • “声明”用词不当。它应该被称为“StatementDetail”,但我不负责实体的命名。帐户就像客户。一个客户有多个支行。这些分支出现在报表详细信息行上。

标签: data-modeling


【解决方案1】:

如果您有 StatementHeader,那么它应该有 AccountID 以保持引用完整性。

但是,最好完全删除 StatementHeader 并将 StatementDate 移动到 Statement 记录中。这将使事情变得更清晰,并使模型更好地描述您想要的内容。

【讨论】:

  • 这只是将冗余转移到记录中,因为每条记录都有相同的声明日期。
  • 它使每个语句记录自包含,这很好。并不是所有的冗余都是不好的——在这种情况下性能会更好,因为你可以消除一个连接来获取日期。我很欣赏在声明日期可能发生变化的情况下,拥有一个“StatementHeader”表允许对日期进行操作,但除非我错了,否则声明日期在这种情况下不会经常改变,所以这是可以接受的。
【解决方案2】:

由于语句是历史的,并且通常是只读的,因此数据有些冗余是可以的。我同意 Richard Harrison 的观点,并将 [AccountID] 和 [StatementDate] 移入 [Statement] 表;我的理由是你说一个帐户有很多分支,所以你将为一个帐户生成一个报表。

将所有这些数据存储在同一个地方会减少连接并加快报告速度,我认为这是这个数据库的原因。

【讨论】:

    【解决方案3】:

    有时,(实际的或感知的)冗余是业务规则的结果。在这种情况下,业务规则是:“向一个帐户发出的报表应仅包含属于该特定帐户的分支机构的交易。”

    要强制执行该规则,您可以尝试提出一个无法违反该规则的数据库架构,或者使用约束或触发器显式执行它。 StatementHeader.AccountID 似乎更容易。在 Oracle 中,你可以这样写:

    create or replace trigger statement_has_unique_account
    before insert or update on Statement
    referencing old as old new as new
    for each row
    declare
      m integer;
      n integer;
    begin
      select b.AccountID
      into m
      from Branch b 
      where b.ID = new.BranchID;
    
      select s.AccountID
      into n
      from StatementHeader s
      where s.ID = new.StatementID;
    
      if m <> n then
        raise_application_error(-1000, 'No way!');
      end if;
    end;
    

    如果 StatementHeader 中没有 AccountID,您将不得不与共享相同 StatementID 的所有其他 Statements 中的所有其他 AccountID 进行比较,从而导致更复杂的语句序列。

    所以我会将 AccountID 作为外键保留在 StatementHeader 中,并使用触发器显式执行业务规则。

    【讨论】:

      猜你喜欢
      • 2020-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多