在表上添加 PERIOD FOR SYSTEM_TIME 失败

got*_*tqn 9 sql-server t-sql temporal-tables sql-server-2016

我有:

  • 包含现有数据的表格
  • SQL Server 2016 SP1
  • SQL Server 管理工作室 17.5

我正在使用以下语句使我的表成为临时表:

ALTER TABLE [dbo].[AnalysisCustomRollupsV2JoinGroups]
ADD [SysStartTime] DATETIME2(0) GENERATED ALWAYS AS ROW START HIDDEN CONSTRAINT DF_AnalysisCustomRollupsV2JoinGroups_SysStart DEFAULT GETUTCDATE()  
   ,[SysEndTime] DATETIME2(0) GENERATED ALWAYS AS ROW END HIDDEN CONSTRAINT DF_AnalysisCustomRollupsV2JoinGroups_SysEnd DEFAULT CONVERT(DATETIME2(0), '9999-12-31 23:59:59'),   
PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime]); 

ALTER TABLE [dbo].[AnalysisCustomRollupsV2JoinGroups]   
SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.AnalysisCustomRollupsV2JoinGroupsChanges));
Run Code Online (Sandbox Code Playgroud)

问题:

在我的本地 SQL 实例上,我有很多数据库;查询在其中一些上成功运行非常奇怪,而在其中一些上它给了我以下错误:

消息 13542,级别 16,状态 0,第 51 行 ADD PERIOD FOR SYSTEM_TIME 在表 'dbo.AnalysisCustomRollupsV2JoinGroups' 上失败,因为有打开的记录,期间的开始时间设置为将来的某个值。

有时,当我调试/执行查询时,初始查询运行成功。

我读到,这可能是因为我在表中有现有数据。所以,我改变了这样的逻辑:

  • 创建缓冲表并用所有记录填充它
  • 删除原表中的记录
  • 创建时态表
  • 将记录移回并删除缓冲表

再说一次,在某些数据库上它是可以的,而在其他数据库上则不是。试图解决这个问题,我发现了以下几点:

对于 StartDate,我已经指定了当前的 UTC 日期——这可以是未来的任何日期和时间,但请注意它应该是 UTC 日期。如果我尝试使用 GETDATE,因为我目前处于英国夏令时,我会收到以下错误:Msg 13542, Level 16, State 0, Line 51 ADD PERIOD FOR SYSTEM_TIME on table 'TestAudit.dbo.SomeData'失败,因为有打开的记录,期间的开始时间设置为将来的值。

以上是什么意思?我需要更改机器时间吗?还是因为我的本地机器不在 UTC 时间,我有时会收到此错误?

got*_*tqn 6

我想我已经找到了解决我的问题的方法,但我不会接受这个作为答案,因为我无法解释导致问题的原因并保证这会在任何时候起作用。这是经过大量测试后找到的修复程序,如果有人能在这里带来更多光明,我会很高兴。


我从来没有datetime2精确地使用过。所以,我回到了这种格式的来源datetime2(0)- Alter Non-Temporal Table to be System-Versioned Temporal Table。与我一直使用的脚本的唯一区别是日期时间函数。我使用了GETUTCDATE()因为我不需要那么精确datetime(0)2018-03-15 07:21:02例如),在这个例子中它是 SYSUTCDATETIME(). 所以,我已经改变了它。

我已经创建了一个脚本,它会删除数据库(如果存在),从备份中恢复数据库,然后在循环中执行我的代码(正如我所说,我有时会遇到错误并且很难重现它)。

我已经多次运行该脚本,但失败的次数不同(有时为 70%,有时为 50%,有时低于):

在此处输入图片说明

我发现了为什么 GETUTCDATE 早于 SYSDATETIMEOFFSET?讨论新旧日期时间函数之间的差异。然后构建以下查询:

DECLARE @UTC DATETIME2(0) = GETUTCDATE();
DECLARE @SYSUTC DATETIME2(0) = SYSUTCDATETIME();

WHILE DATEPART(SECOND, @UTC) = DATEPART(SECOND, @SYSUTC)
BEGIN;
    SET @UTC  = GETUTCDATE();
    SET @SYSUTC  = SYSUTCDATETIME();
END;

SELECT @UTC AS [UTC]
      ,@SYSUTC AS [SYS UTC]
      ,DATEPART(SECOND, @UTC) AS [UTC sec]
      ,DATEPART(SECOND, @SYSUTC) AS [SYS UTC sec]
      ,CASE WHEN @UTC < @SYSUTC THEN 1 ELSE 0 END AS [TimeTravelPossible]
      ,CONVERT(DATETIME2(0), @UTC) AS [UTC date]
      ,CONVERT(DATETIME2(0), @SYSUTC) AS [SYS UTC date]
      ,IIF(CONVERT(DATETIME2(0), @UTC) = CONVERT(DATETIME2(0), @SYSUTC), 1, 0) AS [Are The Same];
Run Code Online (Sandbox Code Playgroud)

我只是想检查是否可以datetime2(0)使用 sys 而不是 sys 日期时间函数获得不同的日期。当然这是可能的。

在此处输入图片说明

也许,引擎正在做的检查正在做这样的事情,将其当前日期与我的较新日期进行比较,这导致了错误 - open records with start of period set to a value in the future.

我已经像这样更改了脚本并在昨晚执行了 1000 次 - 没有生成错误。所以,我相信我已经解决了这个特定问题,但我不能确定。

ALTER TABLE [dbo].[AnalysisCustomRollupsV2JoinGroups]
ADD [SysStartTime] DATETIME2 GENERATED ALWAYS AS ROW START HIDDEN CONSTRAINT DF_AnalysisCustomRollupsV2JoinGroups_SysStart DEFAULT SYSUTCDATETIME()  
   ,[SysEndTime] DATETIME2 GENERATED ALWAYS AS ROW END HIDDEN CONSTRAINT DF_AnalysisCustomRollupsV2JoinGroups_SysEnd DEFAULT CONVERT(DATETIME2, '9999-12-31 23:59:59.9999999'),   
PERIOD FOR SYSTEM_TIME ([SysStartTime], [SysEndTime]); 

ALTER TABLE [dbo].[AnalysisCustomRollupsV2JoinGroups]   
SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.AnalysisCustomRollupsV2JoinGroupsChanges));
Run Code Online (Sandbox Code Playgroud)