【问题标题】:How to Handle Optional Columns如何处理可选列
【发布时间】:2011-05-29 19:31:27
【问题描述】:

我的问题与ServiceASpecificFieldServiceBSpecificField 有关。我觉得这两个字段放置不当,因为对于SubscriberServiceMap 表中所有订阅者的service A 的所有记录,ServiceBSpecificField 将具有空值,反之亦然。

如果我在订阅者表中移动这两个字段,那么我将遇到另一个问题。所有仅使用service A 的订阅者在Subscribers.ServiceBSpecificField 中将具有空值。

那么理想情况下应该怎么做呢?

【问题讨论】:

  • 如果有人能给我建议这个问题的具体标题,那也很好,因为我不知道简而言之如何称呼这个问题。这将促使其他人回答,否则当前的标题过于笼统,以至于许多人可能会阻止自己打开这篇文章。
  • 问题应该是“如何处理可选列”
  • @PerformanceDBA,谢谢我更新了标题。

标签: database database-design normalization relational-database


【解决方案1】:

Service_A and _B 表上放置检查约束,例如:

alter table Service_A add constraint chk_A check (ServiceID = 1);
alter table Service_B add constraint chk_B check (ServiceID = 2);

那么你可以加入喜欢

select *
from SubscriberService as x
left join Service_A    as a on (a.SubscriberID = x.SubscriberID and a.ServiceID = x.ServiceID)
left join Service_B    as b on (b.SubscriberID = x.SubscriberID and b.ServiceID = x.ServiceID)

【讨论】:

  • 我曾经想过这个解决方案。这实际上是一个非常好的解决方案。但问题是每次添加服务时我都必须添加一个表。
  • @Ismail -- 是的,添加一个表,但不需要修改任何现有的表。而且只要服务比较少就可以了。
  • 1) 也同意 2) 我不明白 Check 约束是做什么的,请你解释一下 3) 在子类型中复制 ServiceId 的目的是什么?
【解决方案2】:

一个简单的方法是问自己:这些列的值是否根据订阅SubscriberServiceMap表)或服务而变化?

如果“服务 A”的每个订阅者都具有相同的 ServiceASpecificField 值,那么您必须将其移动到 Services 表中。

您预计会有多少这样的领域? ServiceASpecificField、ServiceBSpecificField、C、D……等等?如果数量很大,您可以选择EAV model,这将涉及创建另一个表。

【讨论】:

  • 谢谢。每个订阅者的价值可能不同。让我看看EAV模型是什么。
  • EAV 模型是一个不错的选择。尽管在我们的一个项目中,我们正在实现一个类似的东西,它有一个 SettingKey 表,其中包含 DataTypeDefaultValue 列,然后 SettingValue 表具有 SettingKeyId 列的外键,然后是 SubscriberIdValue 列。我认为它比 EAV 模型或 EAV 模型的改进版本更好。
【解决方案3】:

这是一个简单的超类型-子类型问题,您可以在 5NF 中解决,您不需要 EAV 或改进的 EAV 或 6NF(完整且最终正确的 EAV)。由于 ServiceAColumn 的值取决于特定订阅者对服务的订阅,所以它必须在关联表中。

▶Normalised Data Model◀(内联链接在某些浏览器/版本上不起作用。)

不熟悉关系建模标准的读者可能会发现▶IDEF1X Notation◀ 很有用。

  • 这是一个普通的关系超类型-子类型结构。这是独占的:Service 是独占的一个子类型。

  • 与其他答案相比,此模型中的关系和子类型更加明确和可控。例如。 FK 关系特定于 Service 子类型,而不是 Service 超类型。

  • 识别任何超类型行是哪个子类型的鉴别器是ServiceTypeServiceType在Subtypes中不需要重复,我们通过子类型表就知道是哪个子类型了。

  • 除非你有数百万个Services,否则短代码比无意义的数字更适合PK。

其他

  • 您可能会丢失 SubscriberService 中的 Id 列,因为它是 100% 冗余且没有任何用途。

  • SubscriberService 的 PK 是 (SubscriberId, ServiceId),除非您想要重复的行。

  • 请将列名:Subscriber.Id改为SubscriberIdService.IdServiceId。永远不要使用Id 作为列名。对于 PK 和 FK,始终使用完整的列名。当您开始编码时,您将清楚其相关性。

第六范式或 EAV

在添加具有新属性的新服务时添加列和表在关系数据库中是必要的,并且您保留了很多控制和完整性。

如果您不“想要”为每个新服务添加新表,那么可以,使用 EAV 或 6NF,但请确保您拥有关系数据库中可用的正常控件(类型安全)以及数据和引用完整性。 EAV 通常在没有适当的关系控制和完整性的情况下实现,这会导致很多很多问题。这是关于该主题的question/answer。如果您确实这样做,并且该问题中的数据模型解释性不够,请告诉我,我会给您一个特定于您要求的数据模型(我上面提供的 DM 是纯 5NF,因为那是您原始问题的全部要求)。

【讨论】:

    【解决方案4】:

    如果 ServiceSpecificField 的值同时取决于服务和订阅者,并且对于所有订阅者-服务对,该字段的类型 - 是相同的(正如我在您的示例中看到的 - 两个字段都为 varchar(50)),那么我会仅更新 SubscriberSerivceMap 表:

    table SubscriberSerivceMap:
    Id
    SubscriberId
    ServiceId
    SpecificField  
    

    此类表的示例:

    Id             SubscriberId       Service Id       SpecifiedField
    1                 1                   1             sub1_serv1
    2                 1                   2             sub1_serv2
    3                 2                   1             sub2_serv1
    4                 2                   2             sub2_serv2
    

    【讨论】:

    • 无法将其移动到服务表,因为每个订阅者的值都不同。
    • 您的意思是该值取决于订阅者和服务类型吗?
    • 两个字段只是举例。每个服务所需的列数可能存在数据类型差异和差异。将来会添加的一些服务可能不需要任何字段。
    • @Alexandr。 -1 您已将两列合并为一列,并破坏了关系和规范化规则。应该如何识别该字段是否包含 ServiceA 或 ServiceB 值?
    猜你喜欢
    • 2015-12-18
    • 1970-01-01
    • 2014-11-20
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    • 1970-01-01
    相关资源
    最近更新 更多