存储历史数据的数据库结构

时间:2010-08-20 17:44:05

标签: sql database database-design

序: 前几天我正在考虑新应用程序的新数据库结构,并意识到我们需要一种以有效的方式存储历史数据的方法。我想让别人看一看,看看这个结构是否有任何问题。我意识到这种存储数据的方法很可能是之前发明的(我几乎可以肯定它已经发明)但是我不知道它是否有一个名字和一些谷歌搜索,我试过没有产生任何东西。

问题: 假设您有一个订单表,订单与下订单的客户的客户表相关。在正常的数据库结构中,您可能期望这样的事情:

orders
------
orderID
customerID


customers
---------
customerID
address
address2
city
state
zip

非常简单,orderID有一个customerID的外键,它是customer表的主键。但是,如果我们要在订单表上运行报表,我们将把客户表加入订单表,这将返回该客户ID的当前记录。如果下订单时,客户地址不同并且随后更改了。现在,我们的订单不再反映订单下达时客户地址的历史记录。基本上,通过更改客户记录,我们只是更改了该客户的所有历史记录。

现在有几种解决方法,其中一种方法是在创建订单时复制记录。我想出的是我认为这样做的一种更简单的方法,可能更优雅一点,并且在任何时候进行更改都有额外的记录功能。

如果我改为使用这样的结构怎么办:

orders
------
orderID
customerID
customerHistoryID


customers
---------
customerID
customerHistoryID


customerHistory
--------
customerHistoryID
customerID
address
address2
city
state
zip
updatedBy
updatedOn

请原谅格式化,但我认为你可以看到这个想法。基本上,我们的想法是,无论何时更改,插入或更新客户,customerHistoryID都会递增,customers表会使​​用最新的customerHistoryID进行更新。订单表现在不仅指向customerID(允许您查看客户记录的所有修订),还指向customerHistoryID,它指向记录的特定修订。现在,订单反映了订单创建时的数据状态。

通过将updatedby和updatedon列添加到customerHistory表,您还可以看到数据的“审核日志”,这样您就可以看到谁进行了更改以及何时进行更改。

一个潜在的缺点可能是删除,但我并不担心这种需要,因为任何东西都不应该被删除。但即使如此,使用activeFlag或类似的东西也可以实现相同的效果,具体取决于数据的域。

我的想法是所有表格都会使用这种结构。无论何时检索历史数据,都将使用customerHistoryID将其连接到历史表,以显示该特定订单的数据状态。

检索客户列表很简单,只需加入customerHistoryID上的customer表即可。

任何人都可以从设计的角度看待这种方法的任何问题,或者为什么这是坏的性能原因。请记住,无论我做什么,我都需要确保保留历史数据,以便后续记录更新不会更改历史记录。有没有更好的办法?这是一个有名称或任何文档的已知想法吗?

感谢您的帮助。

更新 这是我真正想要的一个非常简单的例子。我真正的应用程序将有“订单”与其他表的几个外键。原点/目的地位置信息,客户信息,设施信息,用户信息等。有人建议我有几次可以将信息复制到订单记录中,我已经多次这样做了,但是这会产生包含数百列的记录,在这种情况下实际上是不可行的。

7 个答案:

答案 0 :(得分:10)

当我遇到这样的问题时,另一种方法是将命令作为历史表。它的功能相同,但更容易理解

orders
------
orderID
customerID
address
City
state
zip



customers
---------
customerID
address
City
state
zip

编辑:如果你喜欢的列数达到很高,你可以随意将它分开。

如果您选择使用其他选项并使用历史记录表,则应考虑使用bitemporal数据,因为您可能需要处理历史数据需要更正的可能性。例如,客户将其当前地址从A更改为B,但您还必须更正当前已完成的现有订单的地址。

此外,如果您使用的是MS SQL Server,则可能需要考虑使用索引视图。这将允许您交换小的增量插入/更新性能减少,以获得大的选择性增加。如果您不使用MS SQL服务器,则可以使用触发器和表来复制它。

答案 1 :(得分:6)

在设计数据结构时,要非常小心地存储正确的关系,而不是与正确的关系相似的东西。如果需要维护订单的地址,那么这是因为地址是订单的一部分,而不是客户。此外,单价是订单的一部分,而不是产品等。

尝试这样的安排:

Customer
--------
CustomerId (PK)
Name
AddressId (FK)
PhoneNumber
Email

Order
-----
OrderId (PK)
CustomerId (FK)
ShippingAddressId (FK)
BillingAddressId (FK)
TotalAmount

Address
-------
AddressId (PK)
AddressLine1
AddressLine2
City
Region
Country
PostalCode

OrderLineItem
-------------
OrderId (PK) (FK)
OrderItemSequence (PK)
ProductId (FK)
UnitPrice
Quantity

Product
-------
ProductId (PK)
Price

etc.

如果您真的需要存储历史记录,例如跟踪订单随时间的变化,那么您应该使用日志或审核表,而不是使用事务表。

答案 2 :(得分:4)

通常,订单只是按订单时的原样存储信息。对于零件号,零件名称和价格以及客户地址和名称等内容尤其如此。然后你不必加入5或6个表来获取可以存储在一个表中的信息。这不是非规范化,因为您实际上需要具有在订单时存在的信息。我认为在订单和订单详细信息中存储此信息(存储订购的单个项目)的可能性也较小,因此在数据意外更改方面风险较小。

您的订单表不会有数百列。由于一对多关系,您将拥有订单表和订单明细表。订单表将包含订单号。客户ID 9即使名称发生变化,您也可以搜索该客户订购的所有商品),客户名称,客户地址(注意您不需要城市州邮政等,将地址放在一个字段中),订购日期和可能的与顶级订单直接相关的其他几个字段。然后你有一个订单详细信息表,其中包含订单号,detail_id,部件号,部件描述(这可以是一系列字段的合并,如大小,颜色等,或者你可以分出最常见的),没有项目,单位类型,单位价格,税金,总价,发货日期,状态。您为每个订购的商品添加了一个条目。

答案 3 :(得分:0)

我自己喜欢保持简单。我会使用两个表,一个客户表和一个客户历史表。如果您在历史记录表中有密钥(例如customerId),则没有理由建立连接表,对该密钥的选择将为您提供所有记录。

您在显示历史记录表时也没有审核信息(例如修改日期,修改过的人等),我希望您想要这个。

所以我的看起来像这样:

CustomerTable  (this contains current customer information)
CustID (distinct non null)
...all customer information fields

CustomerHistoryTable
CustId (not distinct non null)
...all customer information fields
DateOfChange 
WhoChanged

DataOfChagne字段是customer表更改的日期(从此记录中的值)到CustomerTable中值的更新记录中的值

如果您需要在订单时找到客户信息,您只需要一个CustomerID即可订购表。

答案 4 :(得分:0)

您想要的是一个数据仓库。由于数据仓库是OLAP而不是OLTP,因此建议您根据需要使用尽可能多的列来实现目标。在您的情况下,无论用户帐户更新如何,数据仓库中的orders表都会有11个字段,因为它们具有订单的“快照”。

Wiley -The Data Warehouse Toolkit, Second Edition

这是一个好的开始。

答案 5 :(得分:0)

我们的薪资系统在许多表中使用生效日期。 ADDRESSES表以EMPLID和EFFDT为键。这使我们可以在每次员工地址发生变化时进行跟踪。您可以使用相同的逻辑来跟踪客户的历史地址。您的查询只需要包含一个条款,该条款将订单日期与订单时生效的客户地址日期进行比较。例如

select o.orderID, c.customerID, c.address, c.city, c.state, c.zip
from orders o, customers c
where c.customerID = o.customerID
and c.effdt = (
   select max(c1.effdt) from customers c1
   where c1.customerID = c.customerID and c1.effdt <= o.orderdt
)

目标是选择生效日期在订单日期当天或之前的客户中的最新行。同样的策略可用于保存产品价格的历史信息。

答案 6 :(得分:0)

如果你真的对这些问题感兴趣,我只能建议你认真看看“时间数据和关系模型”。

警告1:那里没有SQL,你认为关于关系模型的几乎任何东西都会被称为虚假。有充分的理由。

警告2:你应该思考,并认真思考。

警告3:这本书是关于这个特定系列问题的解决方案应该是什么样子,但正如引言所说,它与现有的任何技术无关。

那就是说,这本书是真正的启蒙。至少,它有助于明确表示此类问题的解决方案不会在今天的SQl中找到,或者在今天的ORM中找不到。