我有一个数据强制之谜。我看到同一查询有两种不同的行为,我不明白为什么。
这是所讨论查询的相关部分的摘录,具有固定值。第一个值在我们的查询中代表“今天”,并设置为具有显式 CAST 的相同数据类型:
-- edited to change dates to ISO 8601 literal format to avoid ambiguity
SELECT DATEDIFF(dd,CAST('2014-03-24' AS SmallDateTime),'0001-01-01')
ISO 8601 日期文字格式引用:https://msdn.microsoft.com/en-us/library/ms187819.aspx
我们在同一个 SQL 服务器实例上有两个不同的数据库。
如您所料,其中一个返回零行。
服务器一返回有关“1/1/0001”的日期范围的错误:
Msg 242, Level 16, State 3, Line 1
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
服务器二返回一个看起来大致正确的值:
-735315
有问题的日期几乎可以肯定是“1/1/0001”,并且它失败了,因为它低于 1753 年 1 月 1 日的最小 SQL 日期时间(https://msdn.microsoft.com/en-us/library/ms187819.aspx)。
根据 datediff ( https://msdn.microsoft.com/en-US/library/ms189794(v=SQL.105).aspx ) 的 MSDN 页面,它可以接受以下值:
startdate is an expression that can be resolved to a time, date, smalldatetime, datetime, datetime2, or datetimeoffset
每个服务器的施法结果在服务器之间是相同的,列于此处:
SELECT CAST('0001-01-01' As time) -- works: 00:00:00.0000000
SELECT CAST('0001-01-01' As date) -- works: 0001-01-01
SELECT CAST('0001-01-01' As smalldatetime) -- error: The conversion of a varchar data type to a smalldatetime data type resulted in an out-of-range value.
SELECT CAST('0001-01-01' As DateTime) -- error: The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
SELECT CAST('0001-01-01' As datetime2) -- works: 0001-01-01 00:00:00.0000000
SELECT CAST('0001-01-01' As datetimeoffset) -- works: 0001-01-01 00:00:00.0000000 +00:00
该错误明确指出失败的 DateTime 转换,因此这似乎是对数据库一的强制选择。
似乎数据库二使用了不同的强制转换,成功并正确地进行了 datediff 数学计算。
因为这两个数据库都在同一个 SQL 实例上,所以我排除了实例设置。
以下是我们想检查的一些数据库设置,它们在两个数据库之间看起来也相同(在 SQL Server Management Studio 中检查过):
数据库整理(应按服务器进行,但为清楚起见包括在内):
(database) > Right Click > Properties > General > Maintenance > Collation
Database one: SQL_Latin1_General_CP1_CI_AS
Database two: SQL_Latin1_General_CP1_CI_AS
启用日期关联优化:
(database) > Right Click > Properties > Options > Misc. > Date Correlation Optimization Enabled
Database one: False
Database two: False
两位数年份截止值:
(database) > Right Click > Properties > Options > Containment > Two Digit Year Cutoff
Database one: 2049
Database two: 2049
用户选项日期格式
DBCC USEROPTIONS
Database one, dateformat: mdy
Database two, dateformat: mdy
(other settings appear identical)
我很乐意提供其他设置或测试查询结果,让我知道您希望看到什么。
为什么两个数据库对这个相同的查询表现不同?为什么看起来选择的强制转换不同?
编辑:
- 将查询转换为 ISO 日期文字,以避免在格式上出现任何歧义。仍然看到相同的行为。
- 添加了 DBCC USEROPTOINS 检查日期格式,两者都是 mdy
最佳答案
正如 Aaron Bertrand 在 DBA 堆栈交换站点上的回答,这 Root 于兼容性级别。
SELECT compatibility_level
FROM sys.databases WHERE name = 'FirstDatabase'
90
对比
SELECT compatibility_level
FROM sys.databases WHERE name = 'SecondDatabase'
110
Aaron 对这个问题的原因写得很好: https://dba.stackexchange.com/questions/44908/what-is-the-actual-behavior-of-compatibility-level-80
查看涉及新日期/时间类型的转换
更高的兼容性级别可能意味着 DateDiff 使用 DateTime2 或其他“更广泛”的数据类型并且可以工作。在 90 或以下,它可能使用旧的 DateTime,因此存在转换错误。
感谢 Tab Alleman 提出交叉发布的建议。
关于sql-server - 同一 SQL 实例上数据库之间的 SQL DATEDIFF 强制差异?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29238048/