我正在尝试执行以下操作:
SELECT report.*,
doc.*
FROM report
FOR system_time ALL report
JOIN Document
FOR system_time as of <<report.BeginDate>> doc ON report.DocumentId = doc.DocumentId
基本上我想获取父表的所有历史记录,以及根据父行在正确时间关联的子表。这可能吗?
不幸的是,
FOR SYSTEM_TIME
仅允许文字或变量/参数。它不能是查询中的列。但是您可以将其放入内联表值函数中,然后APPLY
该函数:
CREATE OR ALTER FUNCTION dbo.DocumentAsOfDate (@documentId int, @asOfDate datetime(7))
RETURNS TABLE
AS RETURN
SELECT d.*
FROM dbo.Document FOR SYSTEM_TIME AS OF @asOfDate AS d
WHERE d.DocumentId = @documentId;
GO
SELECT
report.*,
doc.*
FROM report FOR SYSTEM_TIME ALL report
CROSS APPLY dbo.DocumentAsOfDate (report.DocumentId, report.BeginDate) doc;
您可能需要考虑使用哪个日期(开始或结束),以及是否使用
FOR SYSTEM_TIME BETWEEN...
或其他过滤器。
这些都是有趣的解决方案,但它们确实没有回答一般问题。如何在 TSQL 中编写临时连接?如果微软能提供一些这方面的文档就好了,但他们没有。
关键是FOR system_time ALL返回所有组合。您需要外连接,因为一侧或另一侧可能在任何给定时间点缺少值。现在你要处理两个条件。一个在另一个的开始和结束之间开始,反之亦然。
为了获得额外的分数,请删除 For System_Time All 并将查询放入视图中。当在没有系统时间的情况下调用视图时(或指定日期时),仅返回截至该时间的状态。当使用任何其他选项时,将返回相应的行。行可能从连接的一侧或另一侧返回。
我还没有完全测试过这个,可能有我没有想到的场景。
这是我的解决方案:
SELECT report.*, doc.*,
Greatest(report.[SysStart], doc.[SysStart]) As [SysStart],
Least(report.[SysEnd], doc.[SysEnd]) As [SysEnd],
FROM report FOR system_time ALL report
JOIN Document FOR system_time ALL doc
ON report.DocumentId = doc.DocumentId AND
((report.[SysStart] >= doc.[SysStart] AND report.[SysStart] < doc.[SysEnd]) OR
(doc.[SysStart] >= report.[SysStart] And doc.[SysStart] < report.[SysEnd]))
-- Step Two, make it a View that accepts For System_Time
Create View [TemporalJoin] As
SELECT report.*, doc.*,
Greatest(report.[SysStart], doc.[SysStart]) As [SysStart],
Least(report.[SysEnd], doc.[SysEnd]) As [SysEnd],
FROM [report]
JOIN [Document] doc
ON report.DocumentId = doc.DocumentId AND
((report.[SysStart] >= doc.[SysStart] AND report.[SysStart] < doc.[SysEnd]) OR
(doc.[SysStart] >= report.[SysStart] And doc.[SysStart] < report.[SysEnd]))