如何处理可选列

时间:2010-12-23 06:01:54

标签: database database-design normalization relational-database

Database Schema

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

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

那么理想情况应该做些什么呢?

4 个答案:

答案 0 :(得分:3)

alt text

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)

答案 1 :(得分:2)

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

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

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

  • 这是一个普通的Relational Supertype-Subtype结构。这是独家:Service只有一个子类型。

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

  • Discriminator,用于标识任何超类型行的子类型,是ServiceTypeServiceType不需要在子类型中重复,我们知道子类型表是哪个子类型。

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

其他

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

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

  • 请将列名更改为Subscriber.IdSubscriberId; Service.IdServiceId。切勿使用Id作为列名。对于PK和FK,总是使用完整的列名。当您开始编码时,您的相关性将变得清晰。

第六范式或EAV

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

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

答案 2 :(得分:1)

执行此操作的简单方法是问自己:这些列的值是否根据订阅SubscriberServiceMap表)或服务而有所不同?

如果“服务A”的每个订阅者都具有ServiceASpecificField的相同值,则必须将其移至Services表。

您预计会有多少这样的字段? ServiceASpecificField,ServiceBSpecificField,C,D ......等等?如果数字相当大,你可以选择EAV model,这将涉及创建另一个表。

答案 3 :(得分:-1)

如果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