7

假设我正在为一家保险公司设计一个数据库,其中包含以下两个表:

顾客

CustomerID, CustomerName

政策

PolicyID, CustomerID, EffectiveDate, ExpirationDate

如果客户的保单到期日在今天之后,则该客户被视为“活跃”。自然,我希望构建许多过滤掉不活跃客户的查询,反之亦然。我的争论是是否在Customers表格中添加一个“活动”字段。

一方面,这将提高我的一堆查询的性能,并且看起来更清晰,更容易理解。

另一方面,它必须每天更新。此外,客户是否被认为是活跃的完全取决于策略表中的数据。因此,通过添加“活动”列,我正在创建一个潜在的场景,其中客户被标记为活动但他的政策数据会认为他不活动(反之亦然)。换句话说,我正在创建一个数据可能自相矛盾的设计。

这是不好的做法吗?

4

9 回答 9

6

正如duskwuff 所说,这是一种常见的做法。

但是,非规范化有一些成本 - 您必须想出一种替代方法来保持“活动”列的最新状态,并且该过程将引入更多代码并因此产生失败的机会。

一般来说,我强烈建议仅在您知道自己有性能问题时才进行非规范化。在达到数亿条记录之前,对 Policies 表的附加连接几乎肯定不会对系统性能产生可衡量的影响。如果您担心,请构建一个测试数据库并使用 DBMonster 之类的工具来填充测试数据,然后编写您需要的查询;尽可能优化它们,并且只有在无法获得所需的性能程度时才应考虑非规范化。

于 2013-06-24T20:46:56.537 回答
5

您所描述的是所谓的非规范化数据库模式。只要您可以确保数据保持一致,这是一种用于提高性能的完全有效的技术。

进一步阅读:https ://en.wikipedia.org/wiki/Database_normalization

于 2013-06-24T20:39:41.603 回答
2

我将首先注意到对策略表的潜在查询昂贵的(尽管通过正确使用索引可以大大改善这种情况)。您必须查找给定客户的所有策略并确定是否有任何策略处于活动状态。

但是,您没有提供足够的信息来回答这个问题。

这里有两种不同的场景。客户每天都在添加、删除和修改策略。大多数数据库都致力于保持数据最新的操作问题。大多数查询都在策略级别。有时,您需要对客户是否出于某种原因处于活跃状态进行一些报告。好吧,在这种情况下,为“偶尔”查询修改数据结构似乎不值得。正确的重点是确保数据得到更新。

或者。该数据库每晚加载一次,主要用于报告活跃客户而不是他们的保单详细信息(如果您无论如何都必须加入保单,那么您不会保存任何东西)。没有中间更新,并且大多数查询都查看客户级别并将使用活动标志。好吧,这里似乎值得。

我对您的情况的挑战是您似乎正在设计一个用于事务目的(高度规范化)和报告(通常不太规范化)的数据库。两者不一定混合得很好。这是一个简单的原因。您运行了一个有趣的报告查询,需要 5 分钟。在这五分钟内,一个或多个表被锁定以进行查询。或者,查询需要一个小时,因为更新会定期锁定它正在使用的表。

我认为您需要更多地考虑如何使用该系统。我建议您了解数据集市,尤其是 Ralph Kimball 所描述的维度集市(例如,在他的课程“数据仓库工具包”中)。“数据集市”的概念可能会有所帮助。

您还应该了解触发器,即使在操作系统中也可以使此类信息保持最新。关于计算列和视图,它们是访问不直接存储在列中的常见信息的方法。

于 2013-06-24T21:11:05.630 回答
1

如果您定义的标准是它们处于活动状态的唯一标准,我会说只需检查客户的策略行就可以了。

如果有更多逻辑或一些昂贵的逻辑来计算它们的活动状态,那么最好将其作为流程的一部分进行计算并将其存储在客户表的活动字段中。

可能还有其他一些决定因素,例如您提到的那些,并且实际上取决于架构师来测试并确保一种方式优于另一种方式,或者由于坚定的需要而决定他们更喜欢特定的方式。

这只是我从经验中学到的,并没有真正看到任何暗示其他规则的规则,但这并不意味着它们不存在。

于 2013-06-24T20:39:46.493 回答
1

我想是的。你的数据库设计是矛盾的。始终建议您保持数据库清洁。此外,将 DB 保持在最大范式。

在这里,您可以做一件事,而不是定期更新“活动”标志。首先在表客户中添加列“Active Till”。与在策略中添加记录相比,您需要检查“客户”表中的[到期日期]是否早于正在添加的当前记录中的[到期日期]。如果是,请根据“当前的策略记录”将其更改为日期。如果不是,请不要更改它。当您需要检查用户是否处于活动状态时,只需阅读“Active Till”列并决定。

于 2013-06-24T20:43:36.683 回答
1

我的经验是,您添加了该字段,然后在将来的某个时候,其他人很有可能会使它们不同步。我在数据仓库工作了 5 年,这是一个很难处理的常见问题。
为了解决这个问题,我会考虑做两件事之一——要么:

  • 在数据库中构建一个函数或存储过程,将使用两个日期字段进行计算

或者

  • 在使用的语言中构建一个函数或方法,根据存储的字段计算活动。
于 2013-06-24T20:50:16.743 回答
1

我不会Customers指示“活动”状态的列污染您的表。您所说的“活跃”是“业务定义”,很可能会发生变化。此外,这样的列仅在“截至今天”才有意义。您需要每天(大概在午夜)更新客户表以捕获即将到期的保单,并且您可能需要在保单被取消时更新它。另外,您的模型支持同一客户的多个策略(一件好事),因此维护会很尴尬。

此外,如果您想查找“截至”上个月第一天的所有活跃客户(典型查询)怎么办?您的“活动”状态栏将毫无用处。

所以,我的意见是:保持原样。

于 2013-06-24T20:51:59.433 回答
1

我从你的问题的语气中猜测你已经知道这是一个非常糟糕的主意。除非并且直到您知道自己有性能问题,否则您不应该考虑让自己为同一条信息维护两个不同的存储区而头疼。

您提出的解决方案提供的唯一优势是,它在尝试确定客户是否活跃时提供了一些清晰的表达。为了在不引起更新问题的情况下获得清晰的表达,您可以使用视图:

CREATE VIEW CustomerStatus (CustomerID, IsActive) AS
  SELECT CustomerID, (IIF(MAX(ExpirationDate))) >= GET_TODAYS_DATE()
  GROUP BY CustomerID

(其中 IIF() 和 GET_TODAYS_DATE() 是特定于您正在使用的任何数据库引擎的函数)。现在,您可以检查此表,或将其加入客户,以获取客户的状态。

于 2013-06-24T20:52:57.133 回答
0

除了@duskwuff 的评论...

我目前正在 MS SQL 数据库上经历类似的事情,我建议在 Google 中输入以下术语:

Database Normalization -oracle -mysql -db2

自然,如果您使用 Oracle 或 MySql 将这些语句换成 -microsoft 或类似的东西。其中一些内容有点枯燥,但它帮助我组织了我正在从事的当前数据库项目。

于 2013-06-24T21:32:57.350 回答