【发布时间】:2014-01-27 14:41:34
【问题描述】:
在至少一个应用程序中,我需要将旧版本的记录保存在关系数据库中。当应该更新某些内容时,将添加一个新副本并将旧行标记为非当前行。当应该删除某些内容时,应该将其标记为不是当前的或已删除。
有一个简单的用例:只能在当前时间添加记录的新版本,每行取代一行。这可用于在保存新数据时归档以前的记录。为此,我会在每个表中添加以下列:
VersionTime datetime -- Time when this versions becomes effective
IsCurrent bool -- Indicates whether this version is the most current (and not deleted)
如果您只需要知道记录的最新版本是什么,并且只单独列举单个记录的以前版本,这很好。时间点查询比第二种变体更痛苦。
更通用的变体是:可以在任何指定的有效时间范围内随时添加记录版本。所以我可以声明一个实体的某些设置在 2013 年底之前有效,另一个版本在 2014 年有效,另一个版本从 2015 年开始有效。这既可以用于存档旧数据(如上所述),也可以用于提前计划在未来某个时间使用不同的数据(并将此信息保存为存档)。为此,我会在每个表中添加以下列:
ValidFrom datetime -- Time when this version becomes valid (inclusive)
ValidTo datetime -- Time when this version becomes invalid (exclusive)
第二种方法基本上也可以代表第一种,但很难知道哪个版本是最新的 - 因为您还可以为将来添加版本。此外,ValidFrom/ValidTo 设计能够声明重叠范围,根据定义,ValidFrom 最高的行应适用于这种情况。
现在我想知道如何实施有效的解决方案来管理和查询此类数据。通常,您可以使用任何类型的 WHERE、GROUP BY 和 JOIN 编写任何 SQL 查询来获取您想要的记录。但是应用版本控制后,您需要考虑每条记录的正确版本。因此,不是连接另一个表中记录的每个版本,而是必须添加适当的条件以仅选择在给定时间有效的版本。
一个例子:
SELECT a, b, c
FROM t1
必须改为:
SELECT a, b, c
FROM t1
WHERE t1.ValidFrom <= :time AND t1.ValidTo > :time
ORDER BY t1.ValidFrom
LIMIT 1
表连接更复杂:
SELECT a, b, c
FROM t1
LEFT JOIN t2 ON (t2.a = t1.a)
必须改为:
SELECT a, b, c
FROM t1
LEFT JOIN t2 ON (t2.a = t1.a)
WHERE t1.ValidFrom <= :time AND t1.ValidTo > :time
AND t2.ValidFrom <= :time AND t2.ValidTo > :time
这仍然无法处理选择正确版本的重叠时间跨度。我可以添加一些清理方法来消除重叠的版本时间范围,但我不知道这样做的效率如何。
我正在寻求创建一个类(在我的例子中是 C#),它提供了读取和写入此类版本化记录的方法。编写相对容易,因为查询简单且易于通过事务控制。但是查询需要构建一个 API 来接受 SQL SELECT 查询的每个片段,并智能地构建 SQL 查询以从中执行。该查询方法应仅接受一个附加参数,该参数指定从中获取数据的时间。根据每个实体的有效范围,会为每个实体选择不同的版本。
这些基本上是我关于版本化数据和提供 API 来管理它的不完整想法。你有没有做过这样的事情,想告诉我你的想法?你有另一个行之有效的想法吗?你能给我一些关于如何实现这个 API 的建议吗?虽然理论上我知道该怎么做,但我认为这需要做很多工作,而且我无法估计它的效果如何。
【问题讨论】:
-
我不能假装完全理解你的问题......为什么你没有一个审计表,每次更新、插入或删除发生时,每个表都填充有日期标记的记录- 并从那里获取您的数据?我想我一定是错过了什么。
-
我的理解是否正确:您的意思是复制表并将最近写入的数据保存在一个表中,并将所有以前的版本(带有日期)保存在另一个表中?这只会涵盖第一个更简单的场景,而不是第二个。此外,它没有说明执行时间点查询。
标签: sql database database-design version