假设我正在为一家保险公司设计一个数据库,其中包含以下两个表:
客户
CustomerID, CustomerName
策略
PolicyID, CustomerID, EffectiveDate, ExpirationDate
如果客户的政策的有效期在今天之后,则该客户被视为“有效”。当然,我希望构建许多过滤掉非活动客户的查询,反之亦然。我的争论在于是否在Customers
表中添加“Active”字段。
一方面,这将提高我的一堆查询的性能,它看起来更清晰,更容易理解。
另一方面,它必须每天更新。此外,客户是否被视为活动状态完全取决于策略表中的数据。因此,通过添加“活动”列,我创建了一个潜在的场景,其中客户被标记为活动,但他的策略数据会认为他处于非活动状态(反之亦然)。换句话说,我正在创建一个数据可能与之相矛盾的设计。
这是不好的做法吗?
答案 0 :(得分:6)
正如duskwuff所说,这是一种常见的做法。
然而,非规范化有一些成本 - 您必须提出另一种方法来使“活动”列保持最新,并且该过程将引入更多代码,从而导致失败的机会。
一般情况下,如果您确定性能问题,我强烈建议您仅进行非规范化。在您达到数亿条记录之前,对策略表的额外连接几乎肯定不会对您的系统性能产生可衡量的影响。如果你担心,建立一个测试数据库并使用像DBMonster这样的工具用测试数据填充它,并编写你需要的查询;尽可能地优化它们,并且只有在考虑非规范化时才能获得所需的性能程度。
答案 1 :(得分:5)
您所描述的是所谓的非规范化数据库架构。只要您能确保数据保持一致,这是一种非常有效的技术,可用于提高性能。
答案 2 :(得分:2)
我将首先注意到策略表上的潜在查询是昂贵的(尽管通过正确使用索引可以大大改善这一点)。您必须查找给定客户的所有策略,并确定是否有任何活动。
但是,您没有提供足够的信息来回答这个问题。
以下是两种不同的方案。客户每天都会添加,删除和修改策略。大多数数据库都致力于使数据保持最新的操作问题。然后,大多数查询都处于策略级别。有时您需要做一些关于客户是否因某种原因而处于活动状态的报告。那么,在这种情况下,修改“偶尔”查询的数据结构似乎不值得。正确的重点是确保数据得到更新。
可替换地。数据库每晚加载,主要用于活跃客户的报告目的,不的策略详细信息(如果您必须加入策略,那么您不会保存任何内容)。没有中间更新,大多数查询都会查看客户级别,并使用活动标志。好吧,这里似乎值得。
我对您的情况所面临的挑战是,您似乎正在设计数据库,既用于交易目的(高度标准化),也用于报告(通常不太正常化)。这两者不一定非常好。这是一个简单的原因。您运行一个有趣的报告查询,需要五分钟。在这五分钟内,一个或多个表被锁定以进行查询。或者,查询需要一个小时,因为更新会定期锁定正在使用的表。
我认为您需要更多地考虑系统的使用方式。我建议您了解数据集市,尤其是Ralph Kimball(例如,他的班级“数据仓库工具包”)中描述的维度集市。 “数据集市”的概念可能有所帮助。
您还应该了解触发器,即使在操作系统中也可以使这些信息保持最新状态。关于计算列和视图,它们是访问未直接存储在列中的公共信息的方法。
答案 3 :(得分:1)
如果您定义的标准是他们处于活动状态的唯一标准,我会说只需检查客户的“政策”行即可。
如果有更多的逻辑或一些昂贵的逻辑来计算它们的活动状态,那么最好将其计算为进程的一部分并将其存储在Customers表的活动字段中。
可能还有其他一些决定性因素,例如你提到的因素,建筑师要么测试并确保一种方式比另一种方式更好,要么由于坚定而决定他们更喜欢它需要。
这正是我从经验中学到的,并没有真正看到任何其他暗示的规则,但这并不意味着它们不存在。
答案 4 :(得分:1)
我想是的。你的数据库设计是矛盾的。 始终建议您保持DB清洁。另外,请将DB保持在最大正常状态。
这里,您可以做一件事,而不是定期更新“活动”标志。首先在表Customers中添加“Active Till”列。与在策略中添加记录时相比,您需要检查“客户”表中的[到期日期]是否早于添加的当前记录中的[到期日期]。如果是,请根据“当前政策记录”将其更改为日期。如果没有,就不要改变它。当您需要检查用户是否处于活动状态时,只需阅读“Active Till”列并确定。
答案 5 :(得分:1)
我的经验是,你在未来的某个时候添加字段,很有可能其他人会让它们不同步。 我在数据仓库工作了5年,这是一个很难处理的常见问题 为了解决这个问题,我会考虑做两件事之一 - 或者:
或
答案 6 :(得分:1)
我会不使用指示“有效”状态的列污染您的Customers
表。你所谓的“活跃”是一个“业务定义”,可能会改变。而且,这样的列仅仅是有意义的“截至今天”。您需要每天更新客户表(可能是在午夜)以捕获过期策略,并且您可能需要在取消策略时更新它。此外,您的模型支持同一客户的多个策略(一件好事),因此维护会很尴尬。
此外,如果您想在上个月的第一天(典型查询)中找到所有活跃客户,该怎么办?您的“有效”状态列将毫无用处。
所以,我的意见是:保留原样。
答案 7 :(得分:1)
我猜你问题的语气,你已经知道这是一个非常糟糕的主意。除非您知道自己遇到性能问题,否则不应该考虑让自己为同一条信息维护两个不同的商店而头疼。
您提出的解决方案提供的唯一优势是,在尝试确定客户是否处于活动状态时,它提供了一些表达清晰度。为了在不产生更新问题的情况下获得表达清晰度,您可以使用视图:
CREATE VIEW CustomerStatus (CustomerID, IsActive) AS
SELECT CustomerID, (IIF(MAX(ExpirationDate))) >= GET_TODAYS_DATE()
GROUP BY CustomerID
(其中IIF()和GET_TODAYS_DATE()是特定于您正在使用的任何数据库引擎的函数)。现在,您可以查看此表,或将其加入客户,以获取客户的状态。
答案 8 :(得分:0)
除了@duskwuff的评论......
我目前正在MS SQL数据库上浏览something simialr,我建议您在Google中输入以下字词:
Database Normalization -oracle -mysql -db2
当然,如果您使用的是Oracle或MySql,请将这些语句换成-microsoft或类似的东西。其中一些内容有点干,但它帮助我组织了一个我正在研究的当前数据库项目。