视图中的系统版本化(临时)表

时间:2018-07-12 05:34:24

标签: sql-server temporal-tables

我有许多连接的“系统版本”表,例如人,电话号码和电子邮件地址 此人一次只能拥有一个电话号码和一个电子邮件地址。

通常不会在一次同时更新所有3个交易的事务之外通常更新PhoneNumber和EmailAddress。 (但是 可以独立更新,只是在正常情况下不可以) 例如。如果我更改电话号码,则将在同一笔交易中向所有3条记录发送更新,因此在历史记录表中为它们提供相同的“开始时间”。

假设我插入一个人,然后在2笔交易中更改了该人的姓名,电子邮件地址和电话号码:

DECLARE @Id TABLE(ID INT)
DECLARE @PersonId INT

-- Initial insert
BEGIN TRANSACTION
    INSERT INTO Person (Name) OUTPUT inserted.PersonId INTO @Id VALUES ('Homer') 
    SELECT @PersonId = Id FROM @Id
    INSERT INTO EmailAddress (Address, PersonId) VALUES ('homer@fake', @PersonId)
    INSERT INTO PhoneNumber (Number, PersonId) VALUES ('999', @PersonId)
COMMIT TRANSACTION

-- Update 
WAITFOR DELAY '00:00:02'

BEGIN TRANSACTION
    UPDATE Person SET Name = 'Kwyjibo' WHERE PersonID = @PersonId
    UPDATE EmailAddress SET Address = 'kwyjibo@fake'  WHERE PersonID = @PersonId
    UPDATE PhoneNumber SET Number = '000'  WHERE PersonID = @PersonId
COMMIT TRANSACTION

现在,我使用时间查询从视图(只是表的内部联接)中进行选择:

SELECT * FROM vwPerson FOR SYSTEM_TIME ALL 
WHERE PersonId = @PersonId
ORDER BY SysStartTime DESC

对于每种编辑组合,我都会返回一行!

Multi rows

如何查询此视图(如果可能的话),以仅返回1行以获取同一事务中进行的更新?
我可以添加一个WHERE子句以匹配所有SysStartTimes,但这将排除那些表独立于其他2个更新的情况。

1 个答案:

答案 0 :(得分:0)

由于有独立的更新,因此您实际上首先必须“重建”时间轴,可以将数据加入到该时间轴中。接下来是“草图”,显然您的实际表定义没有经过如此测试:

;WITH AllTimes as (
     SELECT PersonId,SysStartTime as ATime FROM Person
     UNION
     SELECT PersonId,SysEndTime FROM Person
     UNION
     SELECT PersonId,SysStartTime FROM EmailAddress
     UNION
     SELECT PersonId,SysEndTime FROM EmailAddress
     UNION
     SELECT PersonId,SysStartTime FROM PhoneNumber
     UNION
     SELECT PersonId,SysEndTime FROM PhoneNumber
), Ordered as (
     SELECT
        PersonId, ATime, ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY Atime) rn
     FROM
        AllTimes
), Intervals as (
    SELECT
       p1.PersonId,
       o1.ATime as StartTime,
       o2.ATime as EndTime
    FROM
       Ordered o1
          inner join
       Ordered o2
          on
              o1.PersonId = o2.PersonId and
              o1.rn = o2.rn - 1
)
SELECT
    * --TODO - Columns
FROM
   Intervals i
      inner join
   Person p
      on
          i.PersonId = p.PersonId and
          i.StartTime < p.SysEndTime and
          p.SysStartTime < i.EndTime
      inner join
   Email e
      on
          i.PersonId = e.PersonId and
          i.StartTime < e.SysEndTime and
          e.SysStartTime < i.EndTime
      inner join
   PhoneNumber pn
      on
          i.PersonId = pn.PersonId and
          i.StartTime < pn.SysEndTime and
          pn.SysStartTime < i.EndTime

如果您只想提供一个人的详细信息,则使用适当的过滤器,优化器有望将其解决。对于我也错过了的联接,可能还有其他过滤器。

希望您能看到3个CTE如何构建时间表。我们利用UNION消除了第一个重复项。