获取多张表的变更历史

编程语言 2026-07-09

我有三张系统版本化的时态表,我们把它们称作 FOOBARBAZ

它们的关联关系如下:

FOO -> 1-N -> BAR -> 1-N -> BAZ

我想为给定的 FOO 行检索完整的变更历史记录,包括它所关联的 BARBAZ 行的所有变更,作为一个统一的按时间顺序的时间线。

我当前的方法使用 FOR SYSTEM_TIME ALL,并基于时间段的重叠进行连接:

SELECT f.*, b.*, bz.*
FROM FOO FOR SYSTEM_TIME ALL f
JOIN BAR FOR SYSTEM_TIME ALL b
    ON b.FooId = f.FooId
    AND b.StartTime < f.EndTime
    AND b.EndTime   > f.StartTime
JOIN BAZ FOR SYSTEM_TIME ALL bz
    ON bz.BarId = b.BarId
    AND bz.StartTime < b.EndTime
    AND bz.EndTime   > b.StartTime
WHERE f.FooId = 1
ORDER BY f.StartTime, b.StartTime, bz.StartTime;

这是正确的做法吗?当三张表并非在完全相同的时间戳更新时,我担心会出现重复行和缺失的变更。

另外,我还注意到 FOO_History 包含在 StartTime 中的值,与子更新相匹配,即使没有任何 FOO 列发生变化——这是预期的吗?

解决方案

在不考虑性能因素的情况下,我认为你的查询基本上按原样成立,但需要做一个小调整,以确保 BAZ 行匹配 FOOBAR 时间段的重叠部分。

SELECT f.*, b.*, bz.*
FROM FOO FOR SYSTEM_TIME ALL f
JOIN BAR FOR SYSTEM_TIME ALL b
    ON b.FooId = f.FooId
    AND b.StartTime < f.EndTime
    AND b.EndTime   > f.StartTime
JOIN BAZ FOR SYSTEM_TIME ALL bz
    ON bz.BarId = b.BarId
    AND bz.StartTime < LEAST(f.EndTime, b.EndTime)         -- Updated
    AND bz.EndTime   > GREATEST(f.StartTime, b.StartTime)  -- Updated
WHERE f.FooId = 1
ORDER BY f.StartTime, b.StartTime, bz.StartTime;

我确实考虑过是否可能更偏向于 ORDER BY GREATEST(f.StartTime, b.StartTime, bz.StartTime),但想不出在哪些场景下会有差异。

至于性能,我在时态表索引方面并非专家,但我相信以下做法会提升查询性能:

CREATE INDEX IX_Bar_FooId ON Bar(FooId, EndTime, StartTime)
CREATE INDEX IX_Baz_BarId ON Baz(BarId, EndTime, StartTime)
CREATE INDEX IX_BarHistory_FooId ON BarHistory(FooId, EndTime, StartTime)
CREATE INDEX IX_BazHistory_BarId ON BazHistory(BarId, EndTime, StartTime)

你也应该了解 列存储索引

虽然变更数据捕捉(Change Data Capture)有其用途,但要实现你想要的结果可能需要更多工作——要么维护扁平化的非规范化历史表,要么手动维护与时态表内置历史表同类的历史表。不过,如果这是用于审计/安全目的,最好有一个与主表分离的安全、只写一次、只读的历史记录。

关于时间戳的一致性,只要使用事务将相关的插入/更新/删除操作分组,这不会成为问题。来自时态表文档,开始时间和结束时间是基于“当前事务的开始时间”来设置的。

关于重复历史,请注意更新语句即使实际数据没有变化,也会生成新的历史记录。举例来说,语句 UPDATE Data SET Value = Value 将生成新的历史记录。

请参阅 这个db<>fiddle 以了解上述两点的示例。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章