【发布时间】:2021-09-28 13:35:42
【问题描述】:
我正在使用一个旧数据库(在 SQL Server 2019 上运行),它使用 datetime 存储时间戳信息的列,我试图更好地处理 SQL Server 在比较datetime 值转换为 datetimeoffset 值。
我们正在尝试将一些代码迁移到将使用 Microsoft SQL Server JDBC 驱动程序的新平台。为了在兼容级别 150(适用于 SQL Server 2019)下运行数据库,Microsoft JDBC 驱动程序会将所有时间戳转换为 datetimeoffset 参数。当datetime 值以3 或7 结尾时,这会导致精度问题。
我一直在阅读其他 Stack Overflow 响应(例如 SQL Server behaviour change while using datetimeoffset 和 [x][2])并了解 SQL Server 如何将 datetime 值强制转换为 datetimeoffset,但这种强制似乎不会正是在 SQL 语句的 WHERE 子句中发生的事情。
以下面的 SQL 为例:
declare @tmp table (id int identity primary key, createDate datetime not null)
insert into
@tmp
(createDate)
values
('2021-09-27 18:36:01.930')
, ('2021-09-27 18:36:01.933')
, ('2021-09-27 18:36:01.937')
declare @input datetimeoffset = '2021-09-27 18:36:01.937'
select
*
from
@tmp
where
createDate = @input
运行此查询时,您没有得到任何匹配项。
我知道,如果你运行 select cast('2021-09-27 18:36:01.937' as datetimeoffset),你会得到 2021-09-27 18:36:01.9370000 +00:00,如果你运行 select cast(cast('2021-09-27 18:36:01.937' as datetime) as datetimeoffset),你会得到 2021-09-27 18:36:01.9366667 +00:00,所以 可能如果createDate 列被隐式转换为datetimeoffset,为什么这些值可能不匹配。但是,当我查看执行计划时,情况似乎并非如此,如果我将代码更改为:
declare @tmp table (id int identity primary key, createDate datetime not null)
insert into
@tmp
(createDate)
values
('2021-09-27 18:36:01.930')
, ('2021-09-27 18:36:01.933')
, ('2021-09-27 18:36:01.937')
declare @input datetimeoffset = '2021-09-27 18:36:01.9366667 +00:00'
select
*
from
@tmp
where
createDate = @input
我仍然没有得到匹配,这似乎意味着 createDate 没有被强制转换为 datetimeoffset 数据类型进行比较。现在,如果我明确地将 datetimeoffset 转换为 datetime,我会得到匹配:
declare @tmp table (id int identity primary key, createDate datetime not null)
insert into
@tmp
(createDate)
values
('2021-09-27 18:36:01.930')
, ('2021-09-27 18:36:01.933')
, ('2021-09-27 18:36:01.937')
declare @input datetimeoffset = '2021-09-27 18:36:01.937'
select
*
from
@tmp
where
createDate = cast(@input as datetime)
或者,如果我尝试查找匹配 2021-09-27 18:36:01.930 值,我不需要显式转换该值:
declare @tmp table (id int identity primary key, createDate datetime not null)
insert into
@tmp
(createDate)
values
('2021-09-27 18:36:01.930')
, ('2021-09-27 18:36:01.933')
, ('2021-09-27 18:36:01.937')
declare @input datetimeoffset = '2021-09-27 18:36:01.930'
select
*
from
@tmp
where
createDate = @input
这告诉我正在进行某种转换,但我无法弄清楚幕后发生了什么。
任何人都可以帮助我了解 SQL Server 在运行以下命令时在后台做什么?
declare @tmp table (id int identity primary key, createDate datetime not null)
insert into
@tmp
(createDate)
values
('2021-09-27 18:36:01.930')
, ('2021-09-27 18:36:01.933')
, ('2021-09-27 18:36:01.937')
declare @input datetimeoffset = '2021-09-27 18:36:01.937'
select
*
from
@tmp
where
createDate = @input
我正在尝试查看是否有某种方法可以格式化时间戳数据,以便所有现有代码都不会中断。我可以将毫秒精度降低到 1/100(而不是 3.33 毫秒精度),但我想保持尽可能高的精度。我最初开始将datetime 值转换为datetime2 的路径,但由于数据量、索引、统计数据等围绕数据转换所有内容都需要相当长的停机时间。更不用说代码使用了需要重构的各种 datetime 数学技巧。
我知道我可以浏览所有代码,还可以添加从datetimeoffset 到datetime 的显式转换,但是一些代码是由流利的 SQL 构建器生成的,所以这样做也不是很简单。
最后,我知道我可以将值转换为 varchar(23) 字段并允许 SQL Server 以这种方式进行隐式转换,但如果可能的话我想避免这种情况。
所以,如果有人能阐明 SQL Server 在内部做什么来比较 datetime 和 datetimeoffset 的值,我将不胜感激。
RANT — 我真的不知道为什么 Microsoft 更改 SQL Server 2016 以在转换为
datetimeoffset和datetime2。尤其是自从铸造了datetimeoffset(3)或datetime2(3)到datetimeoffset会导致2021-09-27 18:36:01.9370000 +00:00。例如:select cast(cast('2021-09-27 18:36:01.937' as datetime) as datetimeoffset) as [datetime] , cast(cast('2021-09-27 18:36:01.937' as datetimeoffset(3)) as datetimeoffset) as [datetimeoffset(3)] , cast(cast('2021-09-27 18:36:01.937' as datetime2(3)) as datetimeoffset) as [datetime2(3)]结果: |日期时间 |日期时间偏移(3) |日期时间2(3) | |--|--|--| | 2021-09-27 18:36:01.9366667 +00:00 | 2021-09-27 18:36:01.9370000 +00:00 | 2021-09-27 18:36:01.9370000 +00:00 |
如果他们都是一样的,看起来会更一致 方式。我知道这个改变据说是为了提高精度 准确性,但我的猜测是它导致的问题比它解决的问题要多。
【问题讨论】:
标签: sql-server datetime jdbc