Ril*_*jor 8 sql-server datetime
或者,微软是如何让时间旅行成为可能的?
考虑这个代码:
DECLARE @Offset datetimeoffset = sysdatetimeoffset();
DECLARE @UTC datetime = getUTCdate();
DECLARE @UTCFromOffset datetime = CONVERT(datetime,SWITCHOFFSET(@Offset,0));
SELECT
Offset = @Offset,
UTC = @UTC,
UTCFromOffset = @UTCFromOffset,
TimeTravelPossible = CASE WHEN @UTC < @UTCFromOffset THEN 1 ELSE 0 END;
Run Code Online (Sandbox Code Playgroud)
@Offset
设置在 之前 @UTC
,但它有时具有较晚的值。(我已经在 SQL Server 2008 R2 和 SQL Server 2016 上尝试过这个。你必须运行几次才能捕捉到可疑的事件。)
这似乎不仅仅是四舍五入或缺乏精度的问题。(事实上,我认为舍入是偶尔“修复”问题的原因。)示例运行的值如下:
因此日期时间精度允许 .880 作为有效值。
甚至Microsoft 的 GETUTCDATE 示例也显示 SYS* 值晚于旧方法,尽管 SELECTed较早:
Run Code Online (Sandbox Code Playgroud)SELECT 'SYSDATETIME() ', SYSDATETIME(); SELECT 'SYSDATETIMEOFFSET()', SYSDATETIMEOFFSET(); SELECT 'SYSUTCDATETIME() ', SYSUTCDATETIME(); SELECT 'CURRENT_TIMESTAMP ', CURRENT_TIMESTAMP; SELECT 'GETDATE() ', GETDATE(); SELECT 'GETUTCDATE() ', GETUTCDATE(); /* Returned: SYSDATETIME() 2007-05-03 18:34:11.9351421 SYSDATETIMEOFFSET() 2007-05-03 18:34:11.9351421 -07:00 SYSUTCDATETIME() 2007-05-04 01:34:11.9351421 CURRENT_TIMESTAMP 2007-05-03 18:34:11.933 GETDATE() 2007-05-03 18:34:11.933 GETUTCDATE() 2007-05-04 01:34:11.933 */
我认为这是因为它们来自不同的底层系统信息。谁能确认并提供详细信息?
Microsoft 的 SYSDATETIMEOFFSET 文档说“SQL Server 通过使用 GetSystemTimeAsFileTime() Windows API 获取日期和时间值”(感谢 srutzky),但他们的GETUTCDATE 文档不太具体,只说“值来自于运行 SQL Server 实例的计算机”。
(这不完全是学术性的。我遇到了一个由此引起的小问题。我正在升级一些程序以使用 SYSDATETIMEOFFSET 而不是 GETUTCDATE,希望在未来获得更高的精度,但我开始得到奇怪的排序,因为其他程序是仍在使用 GETUTCDATE 并且偶尔在日志中“跳过”我的转换过程。)
问题是数据类型粒度/准确性和值来源的组合。
首先,DATETIME
每 3 毫秒才准确/粒度。因此,从更精确的数据类型(例如DATETIMEOFFSET
or )转换DATETIME2
不会只是向上或向下舍入到最接近的毫秒,它可能会相差 2 毫秒。
其次,文档似乎暗示了值的来源不同。SYS* 函数使用高精度 FileTime 函数。
SYSDATETIMEOFFSET文档说明:
SQL Server 使用 GetSystemTimeAsFileTime() Windows API 获取日期和时间值。
而GETUTCDATE文档指出:
此值源自运行 SQL Server 实例的计算机的操作系统。
然后,在About Time文档中,图表显示以下两种(多种)类型:
其他线索在StopWatch
该类的 .NET 文档中(我用粗斜体强调):
秒表通过计算底层计时器机制中的计时器滴答来测量经过的时间。如果安装的硬件和操作系统支持高分辨率性能计数器,则 Stopwatch 类使用该计数器来测量经过的时间。否则,秒表类使用系统计时器来测量经过的时间。
Stopwatch 类使用的计时器取决于系统硬件和操作系统。如果秒表计时器基于高分辨率性能计数器,则IsHighResolution 为true。否则, IsHighResolution 为false,表示秒表计时器基于系统计时器。
因此,存在具有不同精度和不同来源的不同“类型”时间。
但是,即使这是一个非常松散的逻辑,测试这两种类型的函数作为DATETIME
值的来源证明了这一点。来自问题的查询的以下改编显示了这种行为:
DECLARE @Offset DATETIMEOFFSET = SYSDATETIMEOFFSET(),
@UTC2 DATETIME = SYSUTCDATETIME(),
@UTC DATETIME = GETUTCDATE();
DECLARE @UTCFromOffset DATETIME = CONVERT(DATETIME, SWITCHOFFSET(@Offset, 0));
SELECT
Offset = @Offset,
UTC2 = @UTC2,
UTC = @UTC,
UTCFromOffset = @UTCFromOffset,
TimeTravelPossible = CASE WHEN @UTC < @UTCFromOffset THEN 1 ELSE 0 END,
TimeTravelPossible2 = CASE WHEN @UTC2 < @UTCFromOffset THEN 1 ELSE 0 END;
Run Code Online (Sandbox Code Playgroud)
返回:
Offset 2017-06-07 17:50:49.6729691 -04:00
UTC2 2017-06-07 21:50:49.673
UTC 2017-06-07 21:50:49.670
UTCFromOffset 2017-06-07 21:50:49.673
TimeTravelPossible 1
TimeTravelPossible2 0
Run Code Online (Sandbox Code Playgroud)
从上面的结果可以看出,UTC 和 UTC2 都是DATETIME
数据类型。@UTC2
设置 viaSYSUTCDATETIME()
并且设置在之后@Offset
(也取自 SYS* 函数),但在@UTC
设置之前通过GETUTCDATE()
. 然而,@UTC2
似乎来到了之前@UTC
。其中的 OFFSET 部分与任何事情都完全无关。
然而,公平地说,这仍然不是严格意义上的证明。@MartinSmith 跟踪GETUTCDATE()
电话并发现以下内容:
我在这个调用堆栈中看到了三件有趣的事情:
GetSystemTime()
返回一个精确到毫秒的值。SYSDATETIMEOFFSET
。这里有很多关于不同类型时间、不同来源、漂移等的好信息:获取高分辨率时间戳。
实际上,比较不同精度的值是不合适的。例如,如果您有2017-06-07 12:01:58.8770011
and 2017-06-07 12:01:58.877
,那么您如何知道精度较低的值大于、小于或等于精度较高的值?比较它们会假设不那么精确的实际上是2017-06-07 12:01:58.8770000
,但谁知道这是否正确?实际时间可能是2017-06-07 12:01:58.8770005
或2017-06-07 12:01:58.8770111
。
但是,如果您有DATETIME
数据类型,那么您应该使用 SYS* 函数作为源,因为它们更准确,即使由于值被强制转换为不太精确的类型而损失了一些精度。沿着这些路线,使用SYSUTCDATETIME()
而不是SYSDATETIMEOFFSET()
仅通过调用来调整它似乎更有意义SWITCHOFFSET(@Offset, 0)
。