SQL 同一 SQL 实例上的数据库之间的 DATEDIFF 强制差异?
SQL DATEDIFF coercion differences between databases on same SQL instance?
我有一个数据强制之谜。我看到同一查询有两种不同的行为,我不明白为什么。
这是相关查询的相关部分的摘录,具有固定值。第一个值在我们的查询中表示 "today",并设置为具有显式 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 服务器实例上有两个不同的数据库。
其中一个 returns 零行,如您所料。
服务器一 returns 关于“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.
服务器二 returns 一个看起来大致正确的值:
-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 堆栈交换站点上的回答,这植根于兼容性级别。
SELECT compatibility_level
FROM sys.databases WHERE name = 'FirstDatabase'
90
VS
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
查看涉及新 date/time 类型的转化
更高的兼容性级别可能意味着 DateDiff 使用 DateTime2 或其他 'wider' 数据类型并且可以工作。在90或以下,它可能使用旧的DateTime,因此存在转换错误。
感谢 Tab Alleman 提出跨越 post 的建议。
我有一个数据强制之谜。我看到同一查询有两种不同的行为,我不明白为什么。
这是相关查询的相关部分的摘录,具有固定值。第一个值在我们的查询中表示 "today",并设置为具有显式 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 服务器实例上有两个不同的数据库。
其中一个 returns 零行,如您所料。
服务器一 returns 关于“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.
服务器二 returns 一个看起来大致正确的值:
-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 堆栈交换站点上的回答,这植根于兼容性级别。
SELECT compatibility_level
FROM sys.databases WHERE name = 'FirstDatabase'
90
VS
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
查看涉及新 date/time 类型的转化
更高的兼容性级别可能意味着 DateDiff 使用 DateTime2 或其他 'wider' 数据类型并且可以工作。在90或以下,它可能使用旧的DateTime,因此存在转换错误。
感谢 Tab Alleman 提出跨越 post 的建议。