SQL 使用 datetimeoffset 时服务器行为发生变化

SQL Server behaviour change while using datetimeoffset

我使用 SQL 服务器已经有一段时间了,并且注意到在使用 datetimeoffset.

时行为发生了变化

让我们以两个查询为例,一个在日期时间值中使用普通字符串比较,另一个使用 datetimeoffset type viz

select * from (
    select cast('2019-09-06 11:28:05.757' as datetime) as test_date
) data where test_date < '2019-09-06 11:28:05.757'



DECLARE @P1 datetimeoffset;
set @P1 = '2019-09-06 11:28:05.757';

select * from (
    select cast('2019-09-06 11:28:05.757' as datetime) as test_date
) data where test_date < @P1 

我在 SQL Server 2014 和 SQL Server 2019 中执行了上述查询,不同之处在于第二个查询的执行,其中 SQL Server 2019 给出了结果作为相应的日期

我想检查 SQL 服务器的最新版本中关于 datetimeoffset 参数类型的行为是否有一些变化。

测试用例

select cast(test_date as datetimeoffset) d2ofs, @@version
from (
    select cast('2019-09-06 11:28:05.757' as datetime) as test_date
) data 

结果:

d2ofs   (No column name)
2019-09-06 11:28:05.7566667 +00:00  Microsoft SQL Server 2016 (SP2-CU12) (KB4536648) - 13.0.5698.0 (X64) 
    Feb 15 2020 01:47:30 
    Copyright (c) Microsoft Corporation
    Express Edition (64-bit) on Windows Server 2019 Standard 10.0 <X64> (Build 17763: ) (Hypervisor)

d2ofs   (No column name)
2019-09-06 11:28:05.7570000 +00:00  Microsoft SQL Server 2014 (SP3-CU-GDR) (KB4535288) - 12.0.6372.1 (X64) 
    Dec 12 2019 15:14:11 
    Copyright (c) Microsoft Corporation
    Express Edition (64-bit) on Windows NT 6.3 <X64> (Build 17763: ) (Hypervisor)

db<>fiddle

您在 SQL Server 2016's breaking changes.

中预期和记录的行为

Under database compatibility level 130, implicit conversions from datetime to datetime2 data types show improved accuracy by accounting for the fractional milliseconds, resulting in different converted values. Use explicit casting to datetime2 datatype whenever a mixed comparison scenario between datetime and datetime2 datatypes exists. For more information, see this Microsoft Support Article.

在 2016 年,Microsoft 更新了从旧 datetime 数据类型转换为新数据类型的行为,以便更加准确。以前像 2019-09-06 11:28:05.757 这样的 datetime 值转换为 datetime2(7)2019-09-06 11:28:05.7570000,但是,在 2016+ 中,它现在准确地转换为 2019-09-06 11:28:05.7566667。这是因为datetime精确到1/300秒,不是1/1000,所以之前的转换实际上是在值上加上时间。

对于您的查询,您首先有 datetimeoffset(7)2019-09-06 11:28:05.7570000。然后将该值与 datetime2019-09-06 11:28:05.757 进行比较。由于 2 种数据类型不同,一种被隐式转换为另一种。 datetimeoffset 具有更高的优先级,因此 datetime 值 (2019-09-06 11:28:05.757) 被转换为 datetimeoffset2019-09-06 11:28:05.7566667,其中 小于您的其他 datetimeoffset2019-09-06 11:28:05.7570000.

如前所述,在 2014 年(及之前)SQL 服务器错误地将 datetime 值转换为 2019-09-06 11:28:05.7570000,而 不是 小于自己。这就是行为不同的原因。

如果您想要相同的行为,我建议(正如重大更改所做的那样)使用显式转换,然后转换为 datetimeoffset(3)。例如,下面的 not return 任一实例中的结果集:

DECLARE @P1 datetimeoffset;
SET @P1 = '2019-09-06 11:28:05.757';

SELECT *
FROM (SELECT cast('2019-09-06 11:28:05.757' as datetime) as test_date) data
WHERE CONVERT(datetimeoffset(3),test_date) < @P1;

2014, 2019 (Linux)