我应该将此新列添加到customers表还是单独的新表中?

时间:2010-02-12 18:02:20

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

我有一个客户表,其中包含有关我们客户的信息(ID,登录名,姓名,联系信息,不同选项,TS栏等,~15列,〜几百个客户)。

现在我们需要向我们最大的客户发送每日更新(占所有客户的10%)。我需要存储发送给客户的最新更新的时间戳,所以下次我只发送新的更新(我的意思是在TS大于存储的TS的订单行中更新)。

如果我将一个新列“LastUpdatesSentTS”添加到customers表中,那么,据我所知,它将符合规范化规则(如果没有,请提供证明它将破坏规范化的链接)。

但是,正如我所知,从物理数据库设计的角度来看,最好创建一个包含2列[CustomerID,LastUpdatesSentTS]的新表,因为只有不到10%的客户会存储该TS信息。我的意思是,如果我将列添加到customers表 - 大多数客户在该列中将为null。此外,如果我创建一个单独的新表,也许最好从customers表中删除布尔列“SendUpdates”(因为我将能够了解哪些客户需要通过将customers表连接到新表来发送更新)。此外,在这种情况下,我担心在几年内我会有一堆非常小的表,当它们都可以在客户表中时(根据我的理解,不会违反规范化)。

简单来说,我看到了两种可能的表格设计:

1)

Table customers:  
[CustomerID, Name, ..., SendUpdates, LastUpdatesSentTS]

2)

Table customers:  
[CustomerID, Name, ...]  
Table customer_updates_sending:
[CustomerID, LastUpdatesSentTS]

您怎么看?

7 个答案:

答案 0 :(得分:16)

我建议您应该将其作为第二个单独的表格。

原因在于,正如您在问题中所建议的那样,只有大约10%的客户需要这些“更新”,因此“客户”表中大约90%的记录将包含一个始终包含NULL值的字段,如果您在同一客户表上将其作为附加字段执行。将其作为第二个表实现可以避免这个问题。

这不是一个大问题,因为您的客户表非常小,但在设计类似这样的东西时,我会考虑的更重要的问题是面向未来的

基本上我可能会问自己:

  

“我会在将来的任何时候,   需要了解客户的意见   更新的历史而不仅仅是最后一个?“

根据其应用(听起来像是您所说的业务驱动程序),可能需要检查客户更新历史记录。想想管理信息,报告,年度总结等。)

在我写过的几乎所有业务应用程序中,我必须保留至少几年的所有内容(然后通常会将其归档到数据仓库或单独的数据库)目的。

即使您对客户的更新历史不感兴趣,我个人更喜欢2表方法,因为它肯定允许保留历史记录,并提供更好的设计方法(因为只有客户表中的一些记录将需要记录在第二个“更新”表中。但是,请参阅下面的编辑以获取更多信息。如果我知道历史记录永远不需要这些数据,我将在现有客户的表格中作为单个附加字段实施。

另外,不要担心数据库中有“一堆非常小的表”。拥有它们通常是非常充分的理由,并且是object-relational impedance mismatch的一部分,并且通常在应用程序代码中通过更“内聚”的面向对象设计来克服。

修改

(回应对我的回答的评论)。

Aaron Bertrand提出了一个非常有效的观点,如果你有许多额外的数据实例就像这样,并且每次通过主键链接时你不断使用一个单独的表,你最终会有大量非常小的表来保存关于您的一个客户的数据。在查询数据库以便为一个客户提取完整的数据集时,这可能会变得特别麻烦,并且在许多表中过度且低效JOINS会负担过重。

根据“附加”数据的性质,人们必须就如何实施这一数据作出务实的决定。 Aaron建议,在“LastUpdate”日期字段的情况下,在90%的客户表中有很多NULL并不是坏事,我从这里就同意他,从这个角度来看NULL的{​​{1}},这不是一件坏事。我自己建议使用2表方法并不是基于删除NULL的愿望(虽然它确实实现了这一点),而是确保历史可以维持“LastUpdate”日期。

当然,如果完全不需要保留历史记录(并且请记住明天可能需要明天不需要的那些),那么将“LastUpdate”日期作为同一“客户”表中的附加字段实施将会没事的。实际上,如果单个客户和单个“最后更新”日期之间只存在直接的一对一关系,那么将其拆分为2个表将是错误的。在这种情况下,我将它作为customers表上的附加字段实现,因为它现在是该客户的标量值属性。

当然,您现在可以始终在客户表上实现单个字段,如果它成为一个要求(让我们说一年下线)现在保留历史记录的“最后更新”日期,您可以随时重构您的数据库以将其拆分为第二个表格,但请记住,您只是从那一点收集历史数据,因为您将没有前一个记录年的最后更新日期。

如果您执行(或将)需要“上次更新”日期的历史记录(即客户记录和“上次更新”日期记录之间的一对多关系),那么使用第二个表通过主键链接的方法是您唯一的选择。

答案 1 :(得分:6)

“当有疑问时,做最简单的事情可能会奏效” - Ward Cunningham

有时候我会说“添加第二张桌子”,但在这种情况下,我觉得这不合理。据我了解,没有要求维护此属性的值历史记录。桌很小。而且,最终,您所获得的是客户的属性。当然,并非所有这些都会被填充,但对我来说这是次要考虑因素。在大多数情况下,许多字段都具有NULL值,但这并不意味着您必须创建第二个表来保存它们。保持尽可能简单(并且尽可能简化),但不要简单(或者正常:-)。所以,如果是我,我会将这些字段添加到CUSTOMERS表中。 YMMV。

分享并享受。

答案 2 :(得分:3)

我会选择选项2.

我不喜欢像SendUpdates这样的列。 IMO,最好将它存储在不同表中的一行中。

SELECT * FROM customer_updates_sending;

更简单,更快捷
SELECT * FROM  customers WHERE SendUpdates = 1;

对评论作出反应的进一步想法:

是的,如果每个属性与不同且无关的任务或操作相关联,我会主张为其他属性创建其他表。添加与单独任务无关的属性应添加到第一个表(例如买方的中间名)。

在这种情况下,属性(时间戳)与任务(联系客户)相关联。与联系客户相关的所有信息都应该在该表中。 (例如联系地址)。

我不确定你的意思是“你必须在每个表中更改源查询。”表是一组信息。您不在表中保存查询。

您不会对15个联接进行大查询,因为您只需要与您当前正在执行的任何任务有关的联接。如果您没有发送信件,则不需要该信息。当你需要这些信息时,它就会加入。

答案 3 :(得分:1)

答案 4 :(得分:0)

我认为你已经在理解问题和提出选择方面取得了很好的开端。两者都是相当合理的设计,应该可以很好地运作。

我已经看到方法#1失去控制 - 每个新配置值都被添加为一个新字段,每个用户都有许多空配置值,只适用于少数人口。

我更喜欢选项2,因为对我来说这似乎是更直观的方法。

答案 5 :(得分:0)

我想要一种简单的方法来记录我发送的更新。这不是另一个“客户”数据表,而是每个客户发送更新的日期表。您将被问到的问题是,“客户'A'是否在上周二获得了更新?”单表解决方案可以提供的唯一答案是,“我不知道,但他们在星期五得到了它。”可能不被接受。 如果没有历史记录,则无法重新发送客户未获得的更新。

答案 6 :(得分:0)

@CraigTP然后再次,到你的第二点。有更好,更完整的方法来记录记录的历史。至于这个问题,可以在完整的Customer表上完成(并且应该完成)。这基本上称为审计。你有触发器。你可以在hibernate中使用非常简单的方法来开箱即用。最后,我将在现有的表格设计中添加一个新列

编辑我回过头来认为我也错过了SendUpdate部分。恕我直言,那些列也是不合理的(在任何表中都是这样)如果你保留它,它是一个传递性依赖,你应该在3NF中规范化。但无论如何,我相信保持历史记录并保持扩展,添加一个新列是没有SendUpdate列的方法。