我该如何处理这种泛化设计问题?

时间:2011-06-07 08:57:04

标签: sql sql-server database sql-server-2005 database-design

在我们的数据库模型中,我们有一个受益人实体。受益人可以是自然人公司受益人;一个物理受益人有许多属性,如姓名,性别等;此外,受益人(公司或自然人)可以是外国的,也可以不是;这种进一步的区别转化为“共同”属性集的不同域值(例如,在我住的意大利,税ids可能具有与英国税ids不同的数据格式)。

我们现在正在重新设计受益人表,因为开发人员最初从事数据库分析工作。建模做了一个(IMO)短视的选择。他将主键约束放在属性BeneficiaryName上,用于存储公司名称(例如“Microsoft Corporation”)(如果是公司受益人)或姓氏(例如Smith)用于实际受益人。这样我们就有了(不可接受的)约束,我们的数据库中不能有超过1名姓氏为“Smith”(或名为“Smith”的公司)的受益人。

我对这种“重新分解”的方法将引入受益人实体的概括;我会

  1. 清洁受益人表,仅保留常用数据;
  2. 代理主键添加到受益人表中,我们称之为BeneficiaryID;
  3. 拆分受益人表,创建两个子实体( CorporateBeneficiary & PhysicalBeneficiary ,受主要受益人表中的标志区分),与受益人表的1..1关联(外键将引用BeneficiaryID)
  4. 查找(有意义的) CorporateBeneficiary &的主键的 PhysicalBeneficiary
  5. 这应该解决上述BeneficiaryName的唯一性问题。到目前为止似乎还不错?

    我遇到的真正问题是:我该如何/应该如何处理此模型中“外来”属性所增加的进一步复杂性?我应该留下外国,因为它是受益人的旗帜属性?如果是这样,我如何处理对概念上类似的信息(即邮政编码,税号)的重复属性(zipcode_foreign,zipcode,taxid_foreign,taxid等)的不同属性的需求?我真的应该努力将不同的域值纳入一个领域吗?

    欢迎提出任何建议......

4 个答案:

答案 0 :(得分:5)

“清洁受益人表,仅保留常用数据;”

究竟要做什么。

“将一个代理主键添加到受益人表中,我们称之为BeneficiaryID;”

可能有用,但不要忘记,如果存在“自然”标识符,那么也应该强制执行此操作的唯一性。

“拆分受益人表,创建两个子实体(CorporateBeneficiary& PhysicalBeneficiary”

烨。注意到很难强制执行“绝对”数据完整性(在所有NaturalBeneficiaries都是受益人的同时执行,所有非自然受益人也是受益人,并且所有受益人都是自然或非自然受益人)。

“受益人主表格中的旗帜歧视”

不。不会这样做。该标志是冗余的,冗余增加了复杂性而没有增加值。如果您想知道受益人是自然人还是非自然人,请查看记录该事实的表格。

“查找(有意义的)CorporateBeneficiary& PhysicalBeneficiary的主键;”

如果您一般为Benficiaries引入代理,则无需在这些其他表中复制自然标识符。这又是冗余,增加了复杂性而没有增加价值。

“我遇到的真正问题是:我该如何处理这个模型中”外来“属性所引起的进一步复杂化?”“

可以采用相同的方法,区分National和ExtraNational(针对公司和物理Benficiaries),如果数据完整性在关注时至关重要,则可能是从建议到绝对必需的任何方法比方说,至少是国家的Benficiaries。例如,根据国家规定,立法可能适用部队您验证国家SSN号码或国家公司识别号码是否“有效”。如果此类法律适用,则可能至关重要的是,在 in DBMS中检查此类规则,而不仅仅是您的应用。当然,对于非国民来说,通常也不需要进行类似的检查,甚至一般情况下也不可能。

如果您在数据库结构中考虑国家和非国家之间的这种区别,您很可能也想要创建一个“联合”两个(国家和非国家)在一起的视图,然后你必须将您的数据“转换”为“统一”的“通用”格式,这可能只是CHAR(即使您知道,例如,对于National PhysicalBeneficiaries,内容将是他们的SSN号码,您知道它包括一些固定的数字)。

如果您不必在数据库结构中考虑国家和非国家之间的这种区别,那么您将被迫在您的单个表中使用相同的“统一”“通用”格式持有国内和国外的数据。

答案 1 :(得分:2)

在一个字段中容纳不同的域值将在主要受益人表格中复制您现在遇到的问题类型 - 在将来的某个时间点。我会采用这种方法:

在CorporateBeneficiary / PhysicalBeneficiary表中修改您建议的密钥,如下所示:

  1. 按照建议添加代理主键(BeneficiaryID)
  2. 使用适当的数据库或站点机制将BeneficiaryID生成为唯一的生成值。
  3. 使CorporateBeneficiary(CB)和PhysicalBenificiary(PB)的主键相同 - 即BeneficiaryID。
  4. 使CB和PB的主键成为引用受益人表的外键。
  5. 然后对于外国细节采用类似的方法:

    1. 为每个国家/地区添加一个包含公共列的表,但引用相应的域。
    2. 以与CB / PB类似的方式设置新表 - 即与受益人表的关系。
    3. 然后根据左连接创建视图以创建“虚拟表” - 受益人/ PB /外部详细信息。这些视图将成为访问信息的基础。

      如果您有非常大的数据集(> 10 ^ 7行),您可能会发现my answer对验证邮政编码的问题感兴趣。这是一个非常长的回应;并不简单;除非数量非常大,否则是大规模的过度杀伤。

      修改

      如果您的数据量较低,那么@James Anderson建议的多个(例如地址)行的方法将是完全合适的。

答案 2 :(得分:1)

你正在思考正确的路线。

我建议你有一个单独的“地址”表,供所有受益人共享。 (你真的在描述一个对人和公司来说都是一样的maildrop!)。但是,处理i18n问题的最简单方法是保持简单的Line1,Line2,Line3,Line4和国家代码。

我甚至没有特别的邮政编码,因为它们从简单的4位数到英国/ [A-Z0-9] {2,4} [A-Z0-9] { 2,4} /甚至爱尔兰共和国没有任何邮政编码(官方推理“我们不需要这样的东西,我们知道你住在哪里。”)。此外,邮政编码的传统位置因国家/地区而异(英国最终在线上,CH在城市名称之前等等),一些域名遵循城市,县,州其他地方没有的模式有州或县或不在邮件地址使用它们。

答案 3 :(得分:0)

对于概括,有三个概念如何为数据库设计它们:

(1个概括,2个子类型的解释)


<强> 1。所有

的一个表格

这里有一个表中的泛化和子类型的所有属性。

优点:

  • 没有JOIN,不需要UNION(因此它通常是最快的可能性)
  • 方便快捷地从全体人群中获取信息
  • 简单快捷地解决了泛化属性
  • 简单快捷地将事物从子类型属性中解脱出来

缺点:

  • 在具有新属性的新子类型时重新设计表格
  • 许多未使用属性的NULL值
  • 用于标记特定记录的子类型的附加属性
  • (它看起来像丑陋的设计,但往往它更加令人难以置信的快得多......)

PKs / FKs:一个PK,没有FKs

对于您的示例:只有一个“受益人”表。


<强> 2。两张表

仅适用于子类型的表。没有关于概括的表格。

优点:

  • 非常轻松快捷地访问一个特定子类型的整个数据
  • 易于为特定子类型添加新属性

缺点:

  • 很难从整个人口中获取东西(UNION = slow)
  • 很难为泛化添加新属性(所有表都必须更改)

PKs / FKs:每张桌子都有自己的PK,没有FKs

对于您的示例:“CorporateBeneficiary”和“PhysicalBeneficiary”的两个表


第3。三个表

有一个自己的表来进行泛化和每个子类型。 (这是你选择的。)

优点:

  • 轻松访问泛化属性
  • 轻松访问子类型属性
  • 轻松添加新属性

缺点:

  • 获得整个实体的所有属性(JOIN =慢)

的PK / FKS: 理想情况下,所有表都使用一个PK序列。 (即Oracle序列)在子类型表中,pk列同时是泛化表的fk列。 在DBMS中,这可能有点复杂,您没有像Oracle Sequences这样的东西。在那里可能需要在子类型中具有单独的pk和fk列,并且对于每个表具有自己的PK序列。 (即因为序列gen是列属性)


您选择哪一个取决于您的要求。 1.)和3.)你经常会看到2.)的情况非常罕见,很多开发人员都不知道这个设计。

就个人而言,当我没有其他边界条件时,我选择3,因为我发现这是最干净的解决方案。但由于性能原因,我过去也选择了选项1。不记得我从大学毕业后做过类似选项2的事情。 :P