我有一个客户表,其中包含有关我们客户的信息(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]
您怎么看?
答案 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列的方法。