2

我有许多加入的“系统版本化”表,例如 Person、PhoneNumber 和 EmailAddress 该 Person 一次只有一个 PhoneNumber 和一个 EmailAddress。

PhoneNumber 和 EmailAddress通常不会在一次更新所有 3 个的事务之外更新。(但它们可以独立更新,只是不能在正常情况下)例如,如果我更改电话号码,那么所有 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

而且每次编辑组合都会返回一行!

多行

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

4

1 回答 1

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在第一个中消除重复的优势。

于 2018-07-12T05:55:51.163 回答