我有一个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
结果集如下所示:
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
使用 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
我不知道如何做到这一点。或者我愿意接受其他解决方案。我希望能够快速查看每条记录与原始记录相比的变化。
我试图取消结果以比较它们,但我无法让它工作,因为每一行的班次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
查询结果:
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
结束编辑1:
有人可以帮我隔离时间表结果集中每个shiftId的上一条记录中的列中已更改的数据吗?
提前致谢
编辑#2:
以下是我要从此表中“关注更改”的所有列的列表:
[TradeDate] [StatusID] [LastActionDate] [AllowedRankID] [OwnerUserID] [OWNEREMAIL] [OwnerLocationID] [OwnerRankID] [OwnerEmployeeID] [WorkerUserID] [WorkerEmail] [WorkerLocationID] [WorkerRankID] [WorkerPlatoonID] [WorkerEmployeeID] [IsPartialShift] [详情] [LastModifiedByUserID] [存档] [UpdatedDate]
结束编辑2:
我为时态表创建了一个新标签,因为没有一个。如果有更多声望的人想要将其添加到标签的详细信息中,以下是对它们的描述。
答案 0 :(得分:6)
您也可以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;
如果您确实需要列级别的有效性,则可以使用(Demo)
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;
答案 1 :(得分:2)
这肯定不是表现最佳的方式,但符合要求
有没有办法动态执行此操作,以便检测列 自动?
<span >CLICK HOLD to KICK animations</span>
<canvas id="canvas"></canvas>
答案 2 :(得分:1)
方式强>
建议使用使用游标循环遍历行的存储过程,并在临时表中构建结果。 (由于这里有一些可管理的列,我建议手动比较每个列的值,而不是试图动态地进行,因为后者会更复杂。)
<强>演示强>
Rextester演示:http://rextester.com/EEELN72555
存储过程SQL
CREATE PROCEDURE GetChanges(@RequestedShiftID INT)
AS
BEGIN
DECLARE @ValidFrom DATETIME, @ValidTo DATETIME, @TradeDate DATETIME;
DECLARE @PrevTradeDate DATETIME, @LastActionDate DATETIME;
DECLARE @PrevLastActionDate DATETIME;
DECLARE @ShiftId INT, @StatusID INT, @PrevStatusID INT, @OwnerUserID INT;
DECLARE @PrevOwnerUserID INT, @WorkerUserID INT, @PrevWorkerUserID INT;
DECLARE @Archived INT, @PrevArchived INT;
DECLARE @WorkerEmail VARCHAR(MAX), @PrevWorkerEmail VARCHAR(MAX);
CREATE TABLE #Results (Id INT NOT NULL IDENTITY (1,1) PRIMARY KEY, ShiftId INT,
[Column] VARCHAR(255), Value VARCHAR(MAX),
ValidFrom DATETIME, ValidTo DATETIME);
DECLARE cur CURSOR FOR
SELECT
ValidFrom
, ValidTo
, ShiftId
, TradeDate
, StatusID
, [LastActionDate]
, [OwnerUserID]
, [WorkerUserID]
, [WorkerEmail]
, [Archived]
FROM [KrisisShifts_ShiftTrade]
FOR SYSTEM_TIME ALL
WHERE [ShiftID] = @RequestedShiftID
ORDER BY ValidTo Desc;
OPEN cur;
FETCH NEXT FROM cur INTO
@ValidFrom
, @ValidTo
, @ShiftId
, @TradeDate
, @StatusID
, @LastActionDate
, @OwnerUserID
, @WorkerUserID
, @WorkerEmail
, @Archived;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @PrevTradeDate = @TradeDate;
SET @PrevStatusID = @StatusID;
SET @PrevLastActionDate = @LastActionDate;
SET @PrevOwnerUserID = @OwnerUserID;
SET @PrevWorkerUserID = @WorkerUserID;
SET @PrevWorkerEmail = @WorkerEmail;
SET @PrevArchived = @Archived;
FETCH NEXT FROM cur INTO
@ValidFrom
, @ValidTo
, @ShiftId
, @TradeDate
, @StatusID
, @LastActionDate
, @OwnerUserID
, @WorkerUserID
, @WorkerEmail
, @Archived;
IF @TradeDate <> @PrevTradeDate
INSERT INTO #Results (ShiftId, [Column], Value, ValidFrom, ValidTo)
VALUES (@ShiftId, 'TradeDate', @TradeDate, @ValidFrom, @ValidTo);
IF @StatusID <> @PrevStatusID
INSERT INTO #Results (ShiftId, [Column], Value, ValidFrom, ValidTo)
VALUES (@ShiftId, 'StatusID', @StatusID, @ValidFrom, @ValidTo);
IF @LastActionDate <> @PrevLastActionDate
INSERT INTO #Results (ShiftId, [Column], Value, ValidFrom, ValidTo)
VALUES (@ShiftId, 'LastActionDate', @LastActionDate, @ValidFrom, @ValidTo);
IF @OwnerUserID <> @PrevOwnerUserID
INSERT INTO #Results (ShiftId, [Column], Value, ValidFrom, ValidTo)
VALUES (@ShiftId, 'OwnerUserID', @OwnerUserID, @ValidFrom, @ValidTo);
IF @WorkerUserID <> @PrevWorkerUserID
INSERT INTO #Results (ShiftId, [Column], Value, ValidFrom, ValidTo)
VALUES (@ShiftId, 'WorkerUserID', @WorkerUserID, @ValidFrom, @ValidTo);
IF @WorkerEmail <> @PrevWorkerEmail
INSERT INTO #Results (ShiftId, [Column], Value, ValidFrom, ValidTo)
VALUES (@ShiftId, 'WorkerEmail', @WorkerEmail, @ValidFrom, @ValidTo);
IF @Archived <> @PrevArchived
INSERT INTO #Results (ShiftId, [Column], Value, ValidFrom, ValidTo)
VALUES (@ShiftId, 'WorkerEmail', @WorkerEmail, @ValidFrom, @ValidTo);
END
CLOSE cur;
DEALLOCATE cur;
SELECT ShiftId, [Column], Value, ValidFrom, ValidTo
FROM #Results
ORDER BY Id
END;
注意:以上内容仅包含问题示例中的列。在最近的编辑中可能更改的列的列表比这更宽,但其他列当然可以以相同的方式添加。
答案 3 :(得分:1)
关于@Martin Smith "WITH T" 解决方案(2017 年 7 月 1 日 19:31 回答),没有足够的测试数据。 我们可以修改测试数据以在 2017-06-21 00:22:22 更新 OwnerUserID(从 55 到 45)(在 (StatusID = 2) 的现有范围的中间):
VALUES
('2017-06-21 00:26:44', '9999-12-31 23:59:59', 27, '2017-01-27', 3, '2017-01-09 16:23:39.760',45, 34, 'test@hotmail.com', 1),
('2017-06-21 00:22:22', '2017-06-21 00:26:44', 27, '2017-01-27', 2, '2017-01-09 16:23:39.760',45, 34, 'test@hotmail.com', 1),
('2017-06-21 00:19:35', '2017-06-21 00:22:22', 27, '2017-01-27', 2, '2017-01-09 16:23:39.760',55, 34, 'test@hotmail.com', 1),
('2017-06-21 00:19:16', '2017-06-21 00:19:35', 27, '2017-01-28', 3, '2017-01-09 16:23:39.760',55, 34, 'test@hotmail.com', 1)
那么结果是:
ShiftId Column value ValidFrom ValidTo
----------- -------------- ----------- --------------------------- ---------------------------
27 OwnerUserID 55 2017-06-21 00:19:35.0000000 2017-06-21 00:22:22.0000000
27 StatusID 3 2017-06-21 00:19:16.0000000 2017-06-21 00:19:35.0000000
27 StatusID 2 2017-06-21 00:22:22.0000000 2017-06-21 00:26:44.0000000
27 TradeDate 2017-01-28 2017-06-21 00:19:16.0000000 2017-06-21 00:19:35.0000000
结果显示 (StatusID = 2) 的范围不正确。 ValidFrom 日期应为 2017-06-21 00:19:35。 错误来自从与 ValidTo 相同的行中提取 ValidFrom 的查询。
这是我对 Martin 富有洞察力的开端的改进。 它仅通过使用 ValidFrom 起作用。它报告每个值的开始时间。 我们真的不需要显示 ValidTo,因为它只是下一行的 ValidFrom。
USE tempdb
;
DROP TABLE IF EXISTS KrisisShifts_ShiftTrade
;
CREATE TABLE KrisisShifts_ShiftTrade
(
[ValidFrom] DATETIME2,
[ValidTo] DATETIME2,
[ShiftId] INT,
[TradeDate] DATE,
[StatusID] INT,
[LastActionDate] DATETIME2,
[OwnerUserID] INT,
[WorkerUserID] INT,
[WorkerEmail] VARCHAR(16),
[Archived] INT
);
INSERT INTO KrisisShifts_ShiftTrade
([ValidFrom], [ValidTo], [ShiftId], [TradeDate], [StatusID], [LastActionDate], [OwnerUserID],[WorkerUserID],[WorkerEmail], [Archived])
VALUES
('2017-06-21 00:26:44', '9999-12-31 23:59:59', 27, '2017-01-27', 3, '2017-01-09 16:23:39.760',45, 34, 'test@hotmail.com', 1),
('2017-06-21 00:22:22', '2017-06-21 00:26:44', 27, '2017-01-27', 2, '2017-01-09 16:23:39.760',45, 34, 'test@hotmail.com', 1),
('2017-06-21 00:19:35', '2017-06-21 00:22:22', 27, '2017-01-27', 2, '2017-01-09 16:23:39.760',55, 34, 'test@hotmail.com', 1),
('2017-06-21 00:19:16', '2017-06-21 00:19:35', 27, '2017-01-28', 3, '2017-01-09 16:23:39.760',55, 34, 'test@hotmail.com', 1)
;
WITH T
AS (SELECT ValidFrom,
ShiftId,
TradeDate,
StatusID,
LastActionDate,
OwnerUserID,
WorkerUserID,
WorkerEmail,
Archived,
nextTradeDate = LAG(TradeDate) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextStatusID = LAG(StatusID) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextLastActionDate = LAG(LastActionDate) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextOwnerUserID = LAG(OwnerUserID) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextWorkerUserID = LAG(WorkerUserID) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextWorkerEmail = LAG(WorkerEmail) OVER (PARTITION BY ShiftId ORDER BY ValidFrom),
nextArchived = LAG(Archived) OVER (PARTITION BY ShiftId ORDER BY ValidFrom)
FROM KrisisShifts_ShiftTrade)
SELECT ShiftId,
Colname AS [Column],
value,
ValidFrom
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)
ORDER BY ShiftId,
[Column],
ValidFrom
;
这确实包括初始值和当前值(无论好坏)。 每列都有一行显示相同的初始 ValidFrom - 2017-06-21 00:19:16, 每列的最后一行显示当前值。
ShiftId Column value ValidFrom
----------- -------------- -------------------- -------------------
27 Archived 1 2017-06-21 00:19:16
27 LastActionDate 2017-01-09 16:23:39 2017-06-21 00:19:16
27 OwnerUserID 55 2017-06-21 00:19:16
27 OwnerUserID 45 2017-06-21 00:22:22
27 StatusID 3 2017-06-21 00:19:16
27 StatusID 2 2017-06-21 00:19:35
27 StatusID 3 2017-06-21 00:26:44
27 TradeDate 2017-01-28 2017-06-21 00:19:16
27 TradeDate 2017-01-27 2017-06-21 00:19:35
27 WorkerEmail test@hotmail.com 2017-06-21 00:19:16
27 WorkerUserID 34 2017-06-21 00:19:16
但重要的是,它确实正确地表明 (StatusID = 2) 始于 2017-06-21 00:19:35,并在 2017-06-21 00:26:44 被 (StatusID = 3) 取代。 如果您确实需要同时查看 ValidFrom 和 ValidTo 列,您可以将上面的最终查询包装在 CTE 中,并使用 LEAD 函数和“9999-12-31 23:59:59.99”作为“默认”参数进行查询。< /p>
编辑:我刚刚意识到我的解决方案和 Martin 的解决方案不能正确处理主表行被删除然后重新插入的情况。下面的测试数据代表了 (ShiftId = 27) 在 2017-07-22 00:26:55 被删除并在 2017-08-23 00:26:59 稍后重新插入的情况。因此,(StatusID = 3) 在 2017-07-22 00:26:55 和 2017-08-23 00:26:59 之间不存在。对此的适当解决方案将需要一个 ValidFrom 和一个 ValidTo 列,因此我们可以为具有 ValidTo = 2017-07-22 00:26:55 的每一列与另一行匹配的另一行具有 ValidFrom = 2017- 08-23 00:26:59 这样我们就可以看到数据不存在的范围。
VALUES
('2017-08-23 00:26:59', '9999-12-31 23:59:59', 27, '2017-01-27', 3, '2017-01-09 16:23:39.760',45, 34, 'test@hotmail.com', 1),
('2017-06-21 00:26:44', '2017-07-22 00:26:55', 27, '2017-01-27', 3, '2017-01-09 16:23:39.760',45, 34, 'test@hotmail.com', 1),
('2017-06-21 00:22:22', '2017-06-21 00:26:44', 27, '2017-01-27', 2, '2017-01-09 16:23:39.760',45, 34, 'test@hotmail.com', 1),
('2017-06-21 00:19:35', '2017-06-21 00:22:22', 27, '2017-01-27', 2, '2017-01-09 16:23:39.760',55, 34, 'test@hotmail.com', 1),
('2017-06-21 00:19:16', '2017-06-21 00:19:35', 27, '2017-01-28', 3, '2017-01-09 16:23:39.760',55, 34, 'test@hotmail.com', 1)
答案 4 :(得分:0)
- 非常有趣的问题。
- 考虑您想要的结果 - 列“值”应包含不同类型的值(int,decimal,date,binary,varchar,...)。因此,您需要将值转换为varchar,或使用sqlvariant或binary。然后在某些时候,您需要识别值的类型,并针对不同的行进行不同的处理
- 要获取值,您可以尝试使用UNPIVOT:
SELECT someRowID, ValidTo, ValidFrom, col, val
FROM
(SELECT someRowID, ValidTo, ValidFrom /*, ... */,
[TradeDate], [StatusID], [LastActionDate], [AllowedRankID], [OwnerUserID], [OwnerEmail], [OwnerLocationID], [OwnerRankID], [OwnerEmployeeID], [WorkerUserID], [WorkerEmail], [WorkerLocationID], [WorkerRankID], [WorkerPlatoonID], [WorkerEmployeeID], [IsPartialShift], [Detail], [LastModifiedByUserID], [Archived], [UpdatedDate]
FROM ... ) AS p
UNPIVOT
(val FOR col IN ([TradeDate], [StatusID], [LastActionDate], [AllowedRankID], [OwnerUserID], [OwnerEmail], [OwnerLocationID], [OwnerRankID], [OwnerEmployeeID], [WorkerUserID], [WorkerEmail], [WorkerLocationID], [WorkerRankID], [WorkerPlatoonID], [WorkerEmployeeID], [IsPartialShift], [Detail], [LastModifiedByUserID], [Archived], [UpdatedDate])
) AS unpvt
然后类似于UNPIVOT之前的值
...并将结果加入
SELECT ...
FROM prevVals
INNER JOIN vals
ON vals.someRowID = prevVals.someRowID
AND vals.col = prevVals.col
WHERE vals.val <> prevVals.val -- yes, I know here can be a problem (NULLs, types)
这只是一个想法,我希望它会有所帮助
答案 5 :(得分:0)
尽量不使用临时表功能:)。尝试触发来检查更改 - 它更容易,更短。
为所有dml类型(i,u,d)创建包含timestamp和dml类型列(row_id,s__dml_dt,s__dml_type +源表中的所有列)的表的图像。
create trigger dbo.KrisisShifts_ShiftTrade on dbo.KrisisShifts_ShiftTrade
after insert as
begin
insert into dbo.KrisisShifts_ShiftTrade_logtable
select getdate() s__dml_dt, 'i' s__dml_type, * from inserted
-- for udpate select getdate() s__dml_dt, 'i' s__dml_type, * from inserted
-- for delete select getdate() s__dml_dt, 'd' s__dml_type, * from deleted
end
现在,在插入/删除/更新后,您可以检查所有历史值。如果您想要透视结果,可以使用pivot为dbo.KrisisShifts_ShiftTrade_logtable轻松创建视图。
用于记录数据库中所有表的脚本(它将创建前缀为r _的表)。
declare @table sysname
declare @nl varchar(2)
declare @create_table int
declare @cmd varchar(max)
declare @trgname sysname
declare c_tables cursor for
select table_name,
case
when exists (
select 2
from information_schema.tables
where table_name = 'r_'+ot.table_name
) then 0
else 1
end create_table
from information_schema.tables ot
where table_type = 'BASE TABLE'
and table_name not like 'r[_]%'
--and table_name like @tblfilter
open c_tables
fetch next from c_tables into @table,@create_table
while @@fetch_status=0
begin
-- logovaci tabulka
if @create_table=1
begin
set @cmd = 'create table r_'+@table+'(s__row_id int not null identity(1,1),s__dml_dt datetime not null,s__dml_type char(1) not null'
select @cmd = @cmd + char(13)+char(10)+','+column_name+' '+data_type+isnull('('+case when character_maximum_length<0 then 'max' else cast(character_maximum_length as varchar) end+')','')+' null' from information_schema.columns where table_name=@table order by ordinal_position
set @cmd = @cmd + ')'
exec(@cmd)
exec('create index i_s__dml_dt on r_'+@table+' (s__dml_dt)')
end
-- delete trigger
set @trgname = 'trg_'+@table+'_dl_del'
if object_id(@trgname) is not null exec('drop trigger '+@trgname)
exec('
create trigger '+@trgname+' on '+@table+' after delete as
begin
insert into r_'+@table+' select getdate(),''d'',t.* from deleted t
end
')
-- insert trigger
set @trgname = 'trg_'+@table+'_dl_ins'
if object_id(@trgname) is not null exec('drop trigger '+@trgname)
exec('
create trigger '+@trgname+' on '+@table+' after insert as
begin
insert into r_'+@table+' select getdate(),''i'',t.* from inserted t
end
')
-- update trigger
set @trgname = 'trg_'+@table+'_dl_upd'
if object_id(@trgname) is not null exec('drop trigger '+@trgname)
exec('
create trigger '+@trgname+' on '+@table+' after update as
begin
insert into r_'+@table+' select getdate(),''u'',t.* from deleted t
end
')
fetch next from c_tables into @table,@create_table
end
close c_tables
deallocate c_tables
答案 6 :(得分:-2)
你有多少存储空间?
上次我做了这样的事情时,我们在单独的更改日志表中为每个更改的列插入了新行。我们使用客户端逻辑来做到这一点,但你可以通过触发器获得相同的效果。
这占用了大量空间并减慢了写入速度,但它确实为您提供了对更改日志的快速读取权限。
P.S。我们没有通用的解决方案,所以我们只针对需要UI支持的一个表做了。其他一切都使用了伪时态表。 (旧版SQL Server。)