如何设计一个支持某个功能的数据库,该功能允许应用程序用户在某个时间点创建其数据的快照,有点像版本控制.
它将使用户能够返回并查看他们过去的数据.
假设"快照"的数据很复杂并且包括多个表的连接.
我正在寻找一种方法,让每个应用程序用户能够快照他们的数据并返回它.整个数据库快照不是我想要的.
编辑:谢谢你的回答.6NF的答案很有说服力,因为快照数据由于其简单性而去标准化的建议.
澄清:这不是数据仓库问题,也不是关于数据库备份和恢复的问题; 它是关于如何构建一个模式,允许我们在某个时间点捕获一组特定的相关数据的状态.应用程序用户在认为合适时生成快照.用户不会对整个数据库进行快照,只会对他们感兴趣的数据对象进行快照.
这并不容易.
你基本上要求一个时间数据库(Christopher Date称之为第六范式,或6NF).
要为6NF,模式也必须是5NF,并且基本上,对于每个数据,您需要附加该值的数据适用的时间范围.然后在连接中,连接必须仅包括在所考虑的时间范围内的行.
时态建模很难 - 它是第6个普通形式的地址 - 并且在当前的RDBMS中得不到很好的支持.
问题是粒度.第6范式(正如我所理解的)支持时间建模,通过使每个非关键(非关键:,即,在实体上"可以在没有实体失去其身份的情况下改变"的任何东西)成为单独的关系.为此,您可以添加时间戳或时间范围或版本号.使所有内容成为连接可以解决粒度问题,但这也意味着您的查询会更复杂,速度更慢.它还需要确定所有键和非键属性; 这往往是一项巨大的努力.
基本上,无论你有关系("泰德拥有通用汽车股票证书编号为789")添加时间:"泰德拥有通用汽车股票证书编号为789 ,现在 ",这样就可以同时说,"弗雷德拥有通用汽车股票2000年2月3日至昨天的证书,编号为789.显然,这些关系是多对多的(ted现在可以拥有多个证书,并且在他的一生中也可以拥有多个证书),而fred现在可以拥有证书杰克拥有的证书.
因此,我们有一个所有者表,一个股票证书表和一个多对多表,通过id关联所有者和证书.对于多对多表,我们添加了一个start_date和一个end_date.
现在,想象一下每个州/省/土地对股票的股息征税,因此为了税收目的,记录股票证书所有者的居住状态.
所有者居住的地方显然可以独立改变股权; 泰德可以住在内布拉斯加州,购买10股,获得内布拉斯加州的股息,转移到内华达州,向弗雷德出售5股,再购买10股.
但对我们来说,这是泰德可以移动到内布拉斯加州的一段时间,买入10股,在一段时间内,得到了股息在某些时候,这内布拉斯加州的税收,转移到Neveda 在一段时间内,售出500万股弗雷德在一段时间内,购买10 在某个时候更多的股票.
如果我们想要计算内布拉斯加州和内华达州所欠的税款,加入person_stockcertificate和person_address中的匹配/重叠日期范围,我们需要所有这些.一个人的地址不再是一对一的,它是一对多的,因为它是在时间范围内的地址.
如果特德买了10股,我们是否用单个购买日期对买入事件进行建模,或者我们是否为每个股票添加了date_bought?取决于我们需要模型回答的问题.
我们这样做了一次,创建了包含我们想要快照的数据的单独数据库表,但是非规范化,即每个记录包含有意义所需的所有数据,而不是对可能或可能不再存在的id的引用.它还为每一行添加了一个日期.
然后,我们为在所有受影响的表上执行连接的特定插入或更新生成触发器,并将其插入到快照表中.
这样,编写将用户数据恢复到某个时间点的内容将是微不足道的.
如果你有一张桌子:
用户:
id, firstname, lastname, department_id
部门:
id, name, departmenthead_id
您的用户表快照可能如下所示:
user_id, user_firstname, user_lastname, department_id, department_name, deparmenthead_id, deparmenthead_firstname, departmenthead_lastname, snapshot_date
和一个类似的查询
INSERT INTO usersnapshot SELECT user.id AS user_id, user.firstname AS user_firstname, user.lastname AS user_lastname, department.id AS department_id, department.name AS department_name departmenthead.id AS departmenthead_id, departmenthead.firstname AS departmenthead_firstname, departmenthead.lastname AS departmenthead_lastname, GETDATE() AS snapshot_date FROM user INNER JOIN department ON user.department_id = department.id INNER JOIN user departmenthead ON department.departmenthead_id = departmenthead.id
这可确保快照中的每一行在该时刻都为真,即使部门或部门负责人在此期间发生了变化.