J K*_*ing 14 sql t-sql sql-server azure-sql-database temporal-tables
我有一个SQL Azure表,我已经打开了新的Temporal Table功能(SQL Server 2016和SQL Azure v12的新功能).此功能创建另一个表来跟踪主表的所有更改(我在问题的底部包含了关于时态表的文档的链接).您可以使用特殊查询语言来获取此历史记录.请注意以下查询中的FOR SYSTEM_TIME ALL:
SELECT
ValidFrom
, ValidTo
, ShiftId
, TradeDate
, StatusID
, [LastActionDate]
, [OwnerUserID]
, [WorkerUserID]
, [WorkerEmail]
, [Archived]
FROM [KrisisShifts_ShiftTrade]
FOR SYSTEM_TIME ALL
WHERE [ShiftID] = 27
ORDER BY ValidTo Desc
Run Code Online (Sandbox Code Playgroud)
结果集如下所示:
ValidFrom ValidTo ShiftId TradeDate StatusID LastActionDate OwnerUserID WorkerUserID WorkerEmail Archived
--------------------------- --------------------------- ----------- ---------- ----------- ----------------------- ----------- ------------ -------------------------------------------------- --------
2017-06-21 00:26:44.51 9999-12-31 23:59:59.99 27 2017-01-27 3 2017-01-09 16:23:39.760 45 34 test@hotmail.com 1
2017-06-21 00:19:35.57 2017-06-21 00:26:44.51 27 2017-01-27 2 2017-01-09 16:23:39.760 45 34 test@hotmail.com 1
2017-06-21 00:19:16.25 2017-06-21 00:19:35.57 27 2017-01-28 3 2017-01-09 16:23:39.760 45 34 test@hotmail.com 1
Run Code Online (Sandbox Code Playgroud)
使用SYSTEM_TIME FOR ALL临时表从主表(第一个)返回当前记录,其余记录是存储在跟踪表中的该记录的先前版本.(您可以看到validFrom和ValidTo列,显然是记录当前记录的时间)在这种情况下,保留历史记录的跟踪表称为KrisisShifts_ShiftTrade_History
我想构建一个只突出显示每个历史点所做更改的查询.请注意,第二条记录具有不同的StatusID,而第三条记录具有不同的TradeDate
我想生成如下的结果集(我想我会忽略第一个或当前的记录,因为它显然不是chnaged):
期望的结果:
ShiftId Column Value ValidFrom ValidTo
---------- ------------- ------------------- --------------------------- --------------------------
27 StatusId 2 2017-06-21 00:19:35.57 2017-06-21 00:26:44.51
27 TradeDate 2017-01-28 2017-06-21 00:19:35.57 2017-06-21 00:26:44.51
Run Code Online (Sandbox Code Playgroud)
我不知道如何做到这一点.或者我愿意接受其他解决方案.我希望能够快速查看每条记录与原始记录相比的变化.
我试图将结果取消比较以比较它们,但我无法让它工作,因为每一行的班次ID都是相同的.我想在这里展示更多的工作,但我真的很困难.
编辑1:
我已经能够使用lag()在以下查询中仅隔离一列的更改.我可以将此查询与我想要跟踪的每个列的类似的查询结合起来,但是,这是很多工作,必须为每个表构建.有没有办法动态地执行此操作,以便自动检测列?
StatusID更改历史记录查询:(我将记录隔离到shiftId为27,仅用于测试)
SELECT 'SHIFT STATUS' as ColumnName, t1.RecVersion, t1.ShiftID, t1.ValidFrom, t1.ValidTo, t1.StatusId
, (SELECT [Title] FROM [dbo].[KrisisShifts_Status] WHERE [dbo].[KrisisShifts_Status].[StatusID] = t1.StatusId) AS RecStatus
FROM
(SELECT TOP 100 PERCENT
ROW_NUMBER() OVER(PARTITION BY ShiftId ORDER BY ValidTo ASC) AS RecVersion -- reverse sorting the ValidTo date gives "version count" to column changes
, t2.ValidTo
, t2.ValidFrom
, t2.ShiftID
, t2.StatusId
, LAG(StatusId,1,0) OVER (ORDER BY ValidTo DESC) AS PrevStatusId
FROM [KrisisShifts_ShiftTrade]
FOR SYSTEM_TIME ALL AS t2
ORDER BY t2.ValidTo Desc
) AS t1
WHERE
(t1.StatusId <> t1.PrevStatusId)
AND
SHIFTID = 27
ORDER BY t1.ValidTo DESC
Run Code Online (Sandbox Code Playgroud)
查询结果:
ColumnName RecVersion ShiftID ValidFrom ValidTo StatusId RecStatus
------------ -------------------- ----------- --------------------------- --------------------------- ----------- --------------------------------------------------
SHIFT STATUS 3 27 2017-06-21 00:26:44.51 2017-06-25 14:09:32.37 3 Confirmed
SHIFT STATUS 2 27 2017-06-21 00:19:35.57 2017-06-21 00:26:44.51 2 Reserved
SHIFT STATUS 1 27 2017-06-21 00:19:16.25 2017-06-21 00:19:35.57 3 Confirmed
Run Code Online (Sandbox Code Playgroud)
结束编辑1:
有人可以帮我隔离时间表结果集中每个shiftId的上一个记录列中的已更改数据吗?
提前致谢
编辑#2:
以下是我要从此表中"监视更改"的所有列的列表:
[TradeDate] [StatusID] [LastActionDate] [AllowedRankID] [OwnerUserID] [OwnerEmail] [OwnerLocationID] [OwnerRankID] [OwnerEmployeeID] [WorkerUserID] [WorkerEmail] [WorkerLocationID] [WorkerRankID] [WorkerPlatoonID] [WorkerEmployeeID] [IsPartialShift] [详情] [LastModifiedByUserID] [已存档] [UpdatedDate]
结束编辑2:
我为时态表创建了一个新标签,因为没有.如果有更多声望的人想要将其添加到标签的详细信息中,以下是对它们的描述.
您还可以使用CROSS APPLY到UNPIVOT.
应该注意的是,ValidFrom并且ValidTo指的是行版本本身的有效性,而不是列值的必要性.我相信这是你要求的,但这可能令人困惑.
WITH T
AS (SELECT ValidFrom,
ValidTo,
ShiftId,
TradeDate,
StatusID,
LastActionDate,
OwnerUserID,
WorkerUserID,
WorkerEmail,
Archived,
nextTradeDate = LEAD(TradeDate) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextStatusID = LEAD(StatusID) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextLastActionDate = LEAD(LastActionDate) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextOwnerUserID = LEAD(OwnerUserID) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextWorkerUserID = LEAD(WorkerUserID) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextWorkerEmail = LEAD(WorkerEmail) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextArchived = LEAD(Archived) OVER (PARTITION BY ShiftId ORDER BY ValidFrom)
FROM KrisisShifts_ShiftTrade)
SELECT ShiftId,
Colname AS [Column],
value,
ValidFrom,
ValidTo
FROM T
CROSS APPLY ( VALUES
('TradeDate', CAST(TradeDate AS NVARCHAR(4000)), CAST(nextTradeDate AS NVARCHAR(4000))),
('StatusID', CAST(StatusID AS NVARCHAR(4000)), CAST(nextStatusID AS NVARCHAR(4000))),
('LastActionDate', CAST(LastActionDate AS NVARCHAR(4000)), CAST(nextLastActionDate AS NVARCHAR(4000))),
('OwnerUserID', CAST(OwnerUserID AS NVARCHAR(4000)), CAST(nextOwnerUserID AS NVARCHAR(4000))),
('WorkerUserID', CAST(WorkerUserID AS NVARCHAR(4000)), CAST(nextWorkerUserID AS NVARCHAR(4000))),
('WorkerEmail', CAST(WorkerEmail AS NVARCHAR(4000)), CAST(nextWorkerEmail AS NVARCHAR(4000))),
('Archived', CAST(Archived AS NVARCHAR(4000)), CAST(nextArchived AS NVARCHAR(4000)))
) CA(Colname, value, nextvalue)
WHERE EXISTS(SELECT value
EXCEPT
SELECT nextvalue)
AND ValidTo <> '9999-12-31 23:59:59'
ORDER BY ShiftId,
[Column],
ValidFrom;
Run Code Online (Sandbox Code Playgroud)
如果你确实想要列级别的有效性,你可以使用(演示)
WITH T1 AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ShiftId, colname ORDER BY ValidFrom)
- ROW_NUMBER() OVER (PARTITION BY ShiftId, colname, Colvalue ORDER BY ValidFrom) AS Grp,
IIF(DENSE_RANK() OVER (PARTITION BY ShiftId, colname ORDER BY Colvalue) +
DENSE_RANK() OVER (PARTITION BY ShiftId, colname ORDER BY Colvalue DESC) = 2, 0,1) AS HasChanges
FROM KrisisShifts_ShiftTrade
CROSS APPLY ( VALUES
('TradeDate', CAST(TradeDate AS NVARCHAR(4000))),
('StatusID', CAST(StatusID AS NVARCHAR(4000))),
('LastActionDate', CAST(LastActionDate AS NVARCHAR(4000))),
('OwnerUserID', CAST(OwnerUserID AS NVARCHAR(4000))),
('WorkerUserID', CAST(WorkerUserID AS NVARCHAR(4000))),
('WorkerEmail', CAST(WorkerEmail AS NVARCHAR(4000))),
('Archived', CAST(Archived AS NVARCHAR(4000)))
) CA(Colname, Colvalue)
)
SELECT ShiftId, colname, Colvalue, MIN(ValidFrom) AS ValidFrom, MAX(ValidTo) AS ValidTo
FROM T1
WHERE HasChanges = 1
GROUP BY ShiftId, colname, Colvalue, Grp
ORDER BY ShiftId,
colname,
ValidFrom;
Run Code Online (Sandbox Code Playgroud)
这当然不是最好的执行方式,但可以满足要求
有没有办法动态地执行此操作,以便自动检测列?
WITH k
AS (SELECT *,
ROW_NUMBER() OVER (PARTITION BY ShiftId ORDER BY ValidFrom) AS _RN
FROM KrisisShifts_ShiftTrade
/*FOR SYSTEM_TIME ALL*/
),
T
AS (SELECT k.*,
_colname = n.n.value('local-name(.)[1]', 'sysname'),
_colvalue = n.n.value('text()[1]', 'nvarchar(4000)')
FROM k
CROSS apply (SELECT (SELECT k.*
FOR xml path('row'), elements xsinil, type)) ca(x)
CROSS APPLY x.nodes('/row/*[not(self::_RN or self::ValidFrom or self::ValidTo)]') n(n))
SELECT T.ShiftId,
T._colname AS [Column],
T._colvalue AS value,
t.ValidFrom,
T.ValidTo
FROM T T
INNER JOIN T Tnext
ON Tnext._RN = T._RN + 1
AND T.ShiftId = Tnext.ShiftId
AND T._colname = Tnext._colname
WHERE EXISTS(SELECT T._colvalue
EXCEPT
SELECT Tnext._colvalue)
ORDER BY ShiftId,
[Column],
ValidFrom;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4977 次 |
| 最近记录: |